#include "SceneManager.h"
#include "ModelLoader.h"
#include "RenderActor.h"
#include "Logger.h"
#include <d3dcompiler.h>
#include "DxUtils.h"
using namespace std;
using namespace PracEng;

extern float GetGDeltaTimeSec();

// Texture formats
static const DXGI_FORMAT depthStencilTextureFormat = DXGI_FORMAT_R24G8_TYPELESS;
static const DXGI_FORMAT basicColorTextureFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
static const DXGI_FORMAT normalTextureFormat = DXGI_FORMAT_R11G11B10_FLOAT;
static const DXGI_FORMAT specPowTextureFormat = DXGI_FORMAT_R8G8B8A8_UNORM;

// Render view formats
static const DXGI_FORMAT depthStencilRenderViewFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
static const DXGI_FORMAT basicColorRenderViewFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
static const DXGI_FORMAT normalRenderViewFormat = DXGI_FORMAT_R11G11B10_FLOAT;
static const DXGI_FORMAT specPowRenderViewFormat = DXGI_FORMAT_R8G8B8A8_UNORM;

// Resource view formats
static const DXGI_FORMAT depthStencilResourceViewFormat = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
static const DXGI_FORMAT basicColorResourceViewFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
static const DXGI_FORMAT normalResourceViewFormat = DXGI_FORMAT_R11G11B10_FLOAT;
static const DXGI_FORMAT specPowResourceViewFormat = DXGI_FORMAT_R8G8B8A8_UNORM;

static PEVector3 g_AmbientLow(0.2f, 0.2f, 0.2f);
static PEVector3 g_AmbientUppder(0.4f, 0.4f, 0.4f);

static const int SHADOWMAP_SIZE_SPOT = 512;
static const float SHADOWMAP_NEAR = 0.1f;

const PEVector3 GammaToLinear(PEVector3 color)
{
	return PEVector3(color.x * color.x, color.y * color.y, color.z * color.z);
}

SceneManager::SceneManager() :m_DX11Render(nullptr), m_Camera(nullptr), m_pSpotShadowGenVertexCB(nullptr), m_pSceneVertexShaderCB(nullptr), m_pScenePixelShaderCB(nullptr),
m_pSceneVertexShader(nullptr), m_pSceneVSLayout(nullptr), m_pScenePixelShader(nullptr), bShowGbuffer(false), bShowLightVolume(false), m_pShadowGenVSLayout(nullptr),
m_pSpotShadowGenVertexShader(nullptr), m_pPointShadowGenVertexShader(nullptr), m_pPointShadowGenGeometryShader(nullptr), m_pPointShadowGenGeometryCB(nullptr), m_pCascadedShadowGenRS(nullptr),
m_HDRSRV(nullptr), m_HDRRTV(nullptr), m_pHDRTexture(nullptr)
{
	m_pDownScale1DBuffer = nullptr;
	m_pDownScale1DUAV = nullptr;
	m_pDownScale1DSRV = nullptr;
	m_pAvgLumBuffer = nullptr;
	m_pAvgLumUAV = nullptr;
	m_pAvgLumSRV = nullptr;
	m_pDownScaleCB = nullptr;
	m_pFinalPassCB = nullptr;

	m_pDownScaleFirstPassCS = nullptr;
	m_pDownScaleSecondPassCS = nullptr;
	m_pFullScreenQuadVS = nullptr;
	m_pFinalPassPS = nullptr;

	m_pPrevAvgLumBuffer = nullptr;
	m_pPrevAvgLumUAV = nullptr;
	m_pPrevAvgLumSRV = nullptr;

}

SceneManager::~SceneManager()
{
	for (auto DL : m_DirectionalLights)
	{
		delete DL;
		DL = nullptr;
	}

	for (auto PT : m_PointLights)
	{
		delete PT;
		PT = nullptr;
	}

	for (auto SPT : m_SpotLights)
	{
		delete SPT;
		SPT = nullptr;
	}
}

bool SceneManager::Init(DX11Renderer* pRenderer, Camera* pCamera)
{	
	PracEngLog(L"SceneManager Initialize!")
	m_DX11Render = pRenderer;
	m_Camera = pCamera;
	PEVector3 eyePt = PEVector3(0.0f, 0.0f, -110.0f);
	PEVector3 LookAt = PEVector3(0.0f, 0.0f, 0.0f);
	m_Camera->SetViewParams(&eyePt, &LookAt);
	m_Camera->SetProjParames(PracEng_PI / 3.6f, m_DX11Render->GetBackBufferWidth() / (float)m_DX11Render->GetBackBufferHeight(), 0.1f, 500.0f);

	D3D11_BUFFER_DESC cbDesc;
	ZeroMemory(&cbDesc, sizeof(cbDesc));
	cbDesc.Usage = D3D11_USAGE_DYNAMIC;
	cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	cbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	cbDesc.ByteWidth = sizeof(CB_VS_PER_OBJECT);
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&cbDesc, NULL, &m_pSceneVertexShaderCB));
	SetDebugObjectName(m_pSceneVertexShaderCB, "Scene Vertex CB");

	cbDesc.ByteWidth = sizeof(CB_PS_PER_OBJECT);
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&cbDesc, NULL, &m_pScenePixelShaderCB));
	SetDebugObjectName(m_pScenePixelShaderCB, "Scene Pixel CB");

	cbDesc.ByteWidth = sizeof(CB_POINT_LIGHT_DOMAIN);
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&cbDesc, NULL, &m_pPointLightDomainCB));
	SetDebugObjectName(m_pPointLightDomainCB, "Point Light Domain CB");

	cbDesc.ByteWidth = sizeof(CB_POINT_LIGHT_PIXEL);
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&cbDesc, NULL, &m_pPointLightPixelCB));
	SetDebugObjectName(m_pPointLightPixelCB, "Point Light Pixel CB");

	cbDesc.ByteWidth = sizeof(CB_SPOT_LIGHT_DOMAIN);
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&cbDesc, NULL, &m_pSpotLightDomainCB));
	SetDebugObjectName(m_pSpotLightDomainCB, "Spot Light Domain CB");

	cbDesc.ByteWidth = sizeof(CB_SPOT_LIGHT_PIXEL);
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&cbDesc, NULL, &m_pSpotLightPixelCB));
	SetDebugObjectName(m_pSpotLightPixelCB, "Spot Light Pixel CB");

	cbDesc.ByteWidth = sizeof(PEMatrix);
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&cbDesc, NULL, &m_pSpotShadowGenVertexCB));
	SetDebugObjectName(m_pSpotShadowGenVertexCB, "Spot Shadow Gen Vertex CB");

	cbDesc.ByteWidth = 6 * sizeof(PEMatrix);
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&cbDesc, NULL, &m_pPointShadowGenGeometryCB));
	SetDebugObjectName(m_pPointShadowGenGeometryCB, "Point Shadow Gen Vertex CB");

	cbDesc.ByteWidth = 3 * sizeof(PEMatrix);
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&cbDesc, NULL, &m_pCascadedShadowGenGeometryCB));
	SetDebugObjectName(m_pCascadedShadowGenGeometryCB, "Cascade Shadow Gen Vertex CB");
	
	ZeroMemory(&cbDesc, sizeof(cbDesc));
	cbDesc.Usage = D3D11_USAGE_DYNAMIC;
	cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	cbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	cbDesc.ByteWidth = sizeof(CB_DOWN_SCALE);
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&cbDesc, NULL, &m_pDownScaleCB));
	SetDebugObjectName(m_pDownScaleCB, "PostFX - Down Scale CB");

	cbDesc.ByteWidth = sizeof(CB_HDR_TO_LDR);
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&cbDesc, NULL, &m_pFinalPassCB));
	SetDebugObjectName(m_pFinalPassCB, "PostFX - Final Pass CB");

	ID3DBlob* pShaderBlob = NULL;
	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//BasicVS.hlsl", "RenderSceneVS", "vs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateVertexShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pSceneVertexShader));

	// Create Layout
	D3D11_INPUT_ELEMENT_DESC layout[] =
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TANGENT", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	};
	UINT numElements = ARRAYSIZE(layout);
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateInputLayout(layout, ARRAYSIZE(layout), pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), &m_pSceneVSLayout));
	SAFE_RELEASE(pShaderBlob);

	// Create ShadowMap Layout
	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//ShadowGen.hlsl", "SpotShadowGenVS", "vs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateVertexShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pSpotShadowGenVertexShader));

	D3D11_INPUT_ELEMENT_DESC ShadowMaplayout[] =
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
	};

	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateInputLayout(ShadowMaplayout, ARRAYSIZE(ShadowMaplayout), pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), &m_pShadowGenVSLayout));
	SAFE_RELEASE(pShaderBlob);

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//ShadowGen.hlsl", "PointShadowGenVS", "vs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateVertexShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pPointShadowGenVertexShader));
	SAFE_RELEASE(pShaderBlob);

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//ShadowGen.hlsl", "PointShadowGenGS", "gs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateGeometryShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pPointShadowGenGeometryShader));
	SAFE_RELEASE(pShaderBlob);

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//ShadowGen.hlsl", "PointShadowGenVS", "vs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateVertexShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pCascadedShadowGenVertexShader));
	SAFE_RELEASE(pShaderBlob);

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//ShadowGen.hlsl", "CascadedShadowMapsGenGS", "gs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateGeometryShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pCascadedShadowGenGeometryShader));
	SAFE_RELEASE(pShaderBlob);


	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"../../PracticeEngine/HLSL/BasicPS.hlsl", "RenderScenePS", "ps_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreatePixelShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pScenePixelShader));
	SAFE_RELEASE(pShaderBlob);

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//DirectionalLight.hlsl", "DirLightVS", "vs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateVertexShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pDirLightVertexShader));
	SAFE_RELEASE(pShaderBlob);

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//DirectionalLight.hlsl", "DirLightPS", "ps_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreatePixelShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pDirLightPixelShader));
	SAFE_RELEASE(pShaderBlob);

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//GBufferVisual.hlsl", "GBufferVisVS", "vs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateVertexShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pGBufferVisVertexShader));
	SAFE_RELEASE(pShaderBlob);

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//GBufferVisual.hlsl", "GBufferVisPS", "ps_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreatePixelShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pGBufferVisPixelShader));
	SAFE_RELEASE(pShaderBlob);

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//PointLight.hlsl", "PointLightVS", "vs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateVertexShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pPointLightVertexShader));
	SAFE_RELEASE(pShaderBlob);

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//PointLight.hlsl", "PointLightHS", "hs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateHullShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pPointLightHullShader));
	SAFE_RELEASE(pShaderBlob);

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//PointLight.hlsl", "PointLightDS", "ds_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateDomainShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pPointLightDomainShader));
	SAFE_RELEASE(pShaderBlob);

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//PointLight.hlsl", "PointLightPS", "ps_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreatePixelShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pPointLightPixelShader));
	SAFE_RELEASE(pShaderBlob);
	
	//
	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//SpotLight.hlsl", "SpotLightVS", "vs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateVertexShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pSpotLightVertexShader));
	SAFE_RELEASE(pShaderBlob);

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//SpotLight.hlsl", "SpotLightHS", "hs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateHullShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pSpotLightHullShader));
	SAFE_RELEASE(pShaderBlob);

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//SpotLight.hlsl", "SpotLightDS", "ds_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateDomainShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pSpotLightDomainShader));
	SAFE_RELEASE(pShaderBlob);

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//SpotLight.hlsl", "SpotLightPS", "ps_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreatePixelShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pSpotLightPixelShader));
	SAFE_RELEASE(pShaderBlob);

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//common.hlsl", "DebugLightPS", "ps_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreatePixelShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pDebugLightPixelShader));
	SAFE_RELEASE(pShaderBlob);

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//DownScaleCS.hlsl", "DownScaleFirstPass", "cs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateComputeShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pDownScaleFirstPassCS));
	SAFE_RELEASE(pShaderBlob);
	SetDebugObjectName(m_pDownScaleFirstPassCS, "Post FX - Down Scale First Pass CS");

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//DownScaleCS.hlsl", "DownScaleSecondPass", "cs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateComputeShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pDownScaleSecondPassCS));
	SAFE_RELEASE(pShaderBlob);
	SetDebugObjectName(m_pDownScaleSecondPassCS, "Post FX - Down Scale Second Pass CS");

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//DownScaleCS.hlsl", "BloomReveal", "cs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateComputeShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pBloomRevealCS));
	SAFE_RELEASE(pShaderBlob);
	SetDebugObjectName(m_pBloomRevealCS, "Post FX - Bloom Reveal CS");

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//Blur.hlsl", "VerticalFilter", "cs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateComputeShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_VerticalBlurCS));
	SAFE_RELEASE(pShaderBlob);
	SetDebugObjectName(m_VerticalBlurCS, "Post FX - Vertical Blur CS");

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//Blur.hlsl", "HorizFilter", "cs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateComputeShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_HorizontalBlurCS));
	SAFE_RELEASE(pShaderBlob);
	SetDebugObjectName(m_HorizontalBlurCS, "Post FX - Horizontal Blur CS");


	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//HDR.hlsl", "FullScreenQuadVS", "vs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateVertexShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pFullScreenQuadVS));
	SAFE_RELEASE(pShaderBlob);
	SetDebugObjectName(m_pFullScreenQuadVS, "Post FX - Full Screen Quad VS");
	
	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//HDR.hlsl", "FinalPassPS", "ps_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreatePixelShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pFinalPassPS));
	SAFE_RELEASE(pShaderBlob);
	SetDebugObjectName(m_pFinalPassPS, "Post FX - Final Pass PS");

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//SSAO.hlsl", "DepthDownscale", "cs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateComputeShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pSSAODepthDownscaleCS));
	SAFE_RELEASE(pShaderBlob);
	SetDebugObjectName(m_pSSAODepthDownscaleCS, "SSAO - DownSample CS");

	BOOL_RETURN_TRACE_HR(m_DX11Render->LoadShader(L"..//..//PracticeEngine//HLSL//SSAO.hlsl", "SSAOCompute", "cs_5_0", &pShaderBlob));
	BOOL_RETURN_TRACE_HR(m_DX11Render->GetDevice()->CreateComputeShader(pShaderBlob->GetBufferPointer(), pShaderBlob->GetBufferSize(), NULL, &m_pSSAOComputeCS));
	SAFE_RELEASE(pShaderBlob);
	SetDebugObjectName(m_pSSAOComputeCS, "SSAO - Compute Blur CS");

	D3D11_RASTERIZER_DESC rsDesc = {
		D3D11_FILL_SOLID,
		D3D11_CULL_FRONT,
		FALSE,
		D3D11_DEFAULT_DEPTH_BIAS,
		D3D11_DEFAULT_DEPTH_BIAS_CLAMP,
		D3D11_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
		FALSE,
		FALSE,
		FALSE,
		FALSE
	};
	rsDesc.FillMode = D3D11_FILL_SOLID;
	rsDesc.CullMode = D3D11_CULL_BACK;
	TRACE_HR(m_DX11Render->GetDevice()->CreateRasterizerState(&rsDesc, &m_SolidRS));
	
	//m_DX11Render->SetRSState(m_SolidRS);

	rsDesc.CullMode = D3D11_CULL_FRONT;
	TRACE_HR(m_DX11Render->GetDevice()->CreateRasterizerState(&rsDesc, &m_NoDepthClipFrontRS));
	SetDebugObjectName(m_NoDepthClipFrontRS, "No Depth Clip Front RS");

	rsDesc.DepthClipEnable = TRUE;
	rsDesc.CullMode = D3D11_CULL_BACK;
	rsDesc.FillMode = D3D11_FILL_WIREFRAME;
	TRACE_HR(m_DX11Render->GetDevice()->CreateRasterizerState(&rsDesc, &m_WireFrameRS));
	SetDebugObjectName(m_WireFrameRS, "Wire Frame RS");

	rsDesc.FillMode = D3D11_FILL_SOLID;
	rsDesc.DepthBias = 5;
	rsDesc.SlopeScaledDepthBias = 15.0f;
	TRACE_HR(m_DX11Render->GetDevice()->CreateRasterizerState(&rsDesc, &m_pShadowGenRS));
	SetDebugObjectName(m_pShadowGenRS, "Shadow Gen RS");
	
	rsDesc.DepthBias = 5;
	rsDesc.SlopeScaledDepthBias = 90.0f;
	rsDesc.DepthBiasClamp = 0.06f;
	rsDesc.DepthClipEnable = FALSE;
	TRACE_HR(m_DX11Render->GetDevice()->CreateRasterizerState(&rsDesc, &m_pCascadedShadowGenRS));
	SetDebugObjectName(m_pCascadedShadowGenRS, "Cascade Shadow Gen RS");


	D3D11_BLEND_DESC bsDesc = { 0 };
	bsDesc.AlphaToCoverageEnable = false;
	bsDesc.IndependentBlendEnable = false;
	bsDesc.RenderTarget[0].BlendEnable = true;
	bsDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
	bsDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ONE;
	bsDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
	bsDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
	bsDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
	bsDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
	bsDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
	TRACE_HR(m_DX11Render->GetDevice()->CreateBlendState(&bsDesc, &m_GeneralBS));

	
	m_CascadedMatrix.Init(SHADOWMAP_SIZE_SPOT, m_Camera);

	m_nDownScaleGroups = (UINT)ceil((float)(m_DX11Render->GetBackBufferWidth() * m_DX11Render->GetBackBufferHeight() / 16) / 1024.0f);
	m_fMidGray = 0.6f;
	m_fWhite = 1.2f;
	m_fAdaptation = 0.5000f;
	m_fBloomScale = 4.4f;
	m_fBloomThreshold = 1.8f;
	m_fDOFFarStart = 0.2f;
	m_fDOFFarRanceRcp = 0.05f;

	m_iSSAOSampRadius = 10;
	m_fSSAORadius = 13;

	InitGBuffer();

	return false;
}

void SceneManager::AddActor(RenderActor* Actor)
{
	Actors.push_back(Actor);
}

void SceneManager::Update(double DeltaTime)
{
	m_Camera->Tick(DeltaTime);
}

bool SceneManager::Exit()
{
	PracEngLog(L"SceneManager Exit!")
	m_DX11Render = NULL;

	SAFE_RELEASE(m_pSceneVertexShaderCB);
	SAFE_RELEASE(m_pScenePixelShaderCB);
	SAFE_RELEASE(m_pPointLightDomainCB);
	SAFE_RELEASE(m_pPointLightPixelCB);
	SAFE_RELEASE(m_pSpotLightPixelCB);
	SAFE_RELEASE(m_pSpotLightDomainCB);
	SAFE_RELEASE(m_pSpotShadowGenVertexCB);
	SAFE_RELEASE(m_pSceneVertexShader);
	SAFE_RELEASE(m_pSceneVSLayout);
	SAFE_RELEASE(m_pScenePixelShader);
	SAFE_RELEASE(m_pGBufferVisPixelShader);
	SAFE_RELEASE(m_pGBufferVisVertexShader);
	SAFE_RELEASE(m_pShadowGenVSLayout);
	SAFE_RELEASE(m_pSpotShadowGenVertexCB);
	SAFE_RELEASE(m_pSpotShadowGenVertexShader);
	SAFE_RELEASE(m_pPointShadowGenVertexShader);
	SAFE_RELEASE(m_pPointShadowGenGeometryShader);
	SAFE_RELEASE(m_pPointShadowGenGeometryCB);

	SAFE_RELEASE(m_pGBufferUnpackCB);

	// Clear all allocated targets
	SAFE_RELEASE(m_DepthStencilRT);
	SAFE_RELEASE(m_ColorSpecIntensityRT);
	SAFE_RELEASE(m_NormalRT);
	SAFE_RELEASE(m_SpecPowerRT);

	// Clear all views
	SAFE_RELEASE(m_DepthStencilDSV);
	SAFE_RELEASE(m_DepthStencilReadOnlyDSV);
	SAFE_RELEASE(m_ColorSpecIntensityRTV);
	SAFE_RELEASE(m_NormalRTV);
	SAFE_RELEASE(m_SpecPowerRTV);
	SAFE_RELEASE(m_DepthStencilSRV);
	SAFE_RELEASE(m_ColorSpecIntensitySRV);
	SAFE_RELEASE(m_NormalSRV);
	SAFE_RELEASE(m_SpecPowerSRV);

	// Clear the depth stencil state
	SAFE_RELEASE(m_DepthStencilState);

	SAFE_RELEASE(m_pSampLinear);
	SAFE_RELEASE(m_pSampPoint);
	SAFE_RELEASE(m_pPCFSampler);

	SAFE_RELEASE(m_WireFrameRS);
	SAFE_RELEASE(m_SolidRS);
	SAFE_RELEASE(m_NoDepthClipFrontRS);
	SAFE_RELEASE(m_pShadowGenRS);
	SAFE_RELEASE(m_GeneralBS);

	SAFE_RELEASE_ARRAY(m_SpotDepthStencilRT)
	SAFE_RELEASE_ARRAY(m_SpotDepthStencilDSV)
	SAFE_RELEASE_ARRAY(m_SpotDepthStencilSRV)
	SAFE_RELEASE_ARRAY(m_PointDepthStencilRT)
	SAFE_RELEASE_ARRAY(m_PointDepthStencilDSV)
	SAFE_RELEASE_ARRAY(m_PointDepthStencilSRV)
	SAFE_RELEASE_ARRAY(m_pCascadedDepthStencilRT)
	SAFE_RELEASE_ARRAY(m_pCascadedDepthStencilDSV)
	SAFE_RELEASE_ARRAY(m_pCascadedDepthStencilSRV)
	SAFE_RELEASE(m_pCascadedShadowGenVertexShader)
	SAFE_RELEASE(m_pCascadedShadowGenGeometryShader)
	SAFE_RELEASE(m_pCascadedShadowGenGeometryCB)

	SAFE_RELEASE(m_pHDRTexture)
	SAFE_RELEASE(m_HDRRTV)
	SAFE_RELEASE(m_HDRSRV)
	SAFE_RELEASE(m_pDownScale1DBuffer)
	SAFE_RELEASE(m_pDownScale1DUAV)
	SAFE_RELEASE(m_pDownScale1DSRV)
	SAFE_RELEASE(m_pAvgLumBuffer)
	SAFE_RELEASE(m_pAvgLumUAV)
	SAFE_RELEASE(m_pAvgLumSRV)
	SAFE_RELEASE(m_pDownScaleCB)
	SAFE_RELEASE(m_pFinalPassCB)
	SAFE_RELEASE(m_pDownScaleFirstPassCS)
	SAFE_RELEASE(m_pDownScaleSecondPassCS)
	SAFE_RELEASE(m_pFullScreenQuadVS)
	SAFE_RELEASE(m_pFinalPassPS)
	SAFE_RELEASE(m_pPrevAvgLumBuffer)
	SAFE_RELEASE(m_pPrevAvgLumUAV)
	SAFE_RELEASE(m_pPrevAvgLumSRV)

	return true;
}

void SceneManager::RenderScene()
{
	float ClearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
	//ID3D11RenderTargetView* pRTV = m_DX11Render->GetRenderTargetView();
	m_DX11Render->ClearRTView(m_HDRRTV, ClearColor);

	// Store the previous depth state
	ID3D11DepthStencilState* pPrevDepthState;
	UINT nPrevStencil;
	m_DX11Render->GetDSState(&pPrevDepthState, &nPrevStencil);

	// Generate shadow map
	GenerateShadowMap();

	//GBuffer Pass
	PreRendering();
	DrawActors();
	PostRendering();

	DoSSAO();

	DoLighting();

	DoPostProcessing();

	if (bShowLightVolume)
		DebugPointLightVolume();

	if (bShowGbuffer)
		DebugGBuffer();

	m_DX11Render->SetDSState(pPrevDepthState, nPrevStencil);
	SAFE_RELEASE(pPrevDepthState);
}

void SceneManager::VisualizeGBuffer(ID3D11DeviceContext* pd3dImmediateContext)
{
	ID3D11ShaderResourceView* arrViews[4] = { m_DepthStencilSRV, m_ColorSpecIntensitySRV, m_NormalSRV, m_SpecPowerSRV };
	pd3dImmediateContext->PSSetShaderResources(0, 4, arrViews);

	pd3dImmediateContext->PSSetSamplers(0, 1, &m_pSampPoint);

	pd3dImmediateContext->IASetInputLayout(NULL);
	pd3dImmediateContext->IASetVertexBuffers(0, 0, NULL, NULL, NULL);
	pd3dImmediateContext->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);

	// Set the shaders
	pd3dImmediateContext->VSSetShader(m_pGBufferVisVertexShader, NULL, 0);
	pd3dImmediateContext->GSSetShader(NULL, NULL, 0);
	pd3dImmediateContext->PSSetShader(m_pGBufferVisPixelShader, NULL, 0);

	pd3dImmediateContext->Draw(16, 0);

	// Cleanup
	pd3dImmediateContext->VSSetShader(NULL, NULL, 0);
	pd3dImmediateContext->PSSetShader(NULL, NULL, 0);

	ZeroMemory(arrViews, sizeof(arrViews));
	pd3dImmediateContext->PSSetShaderResources(0, 4, arrViews);
}
float tempG1 = 250.0f;
float tempG2 = 0.125f;
void SceneManager::DrawActors()
{
	PEMatrix MatView = *m_Camera->GetViewMatrix();
	PEMatrix MatProj = *m_Camera->GetProjectMatrix();
	PEMatrix mWVP = MatView * MatProj;

	HRESULT hr;
	D3D11_MAPPED_SUBRESOURCE MappedResource;
	hr = m_DX11Render->GetDeviceContext()->Map(m_pSceneVertexShaderCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource);
	if (hr == S_OK)
	{
		CB_VS_PER_OBJECT* pVsPerObject = static_cast<CB_VS_PER_OBJECT*>(MappedResource.pData);
		pVsPerObject->m_mWorldViewProjection = PEMatrix::MakeTranspose(mWVP);
		pVsPerObject->m_mWorld = PEMatrix::MakeTranspose(PEMatrix::GetIdentityMatrix());
		m_DX11Render->GetDeviceContext()->Unmap(m_pSceneVertexShaderCB, 0);
		m_DX11Render->GetDeviceContext()->VSSetConstantBuffers(0, 1, &m_pSceneVertexShaderCB);
	}

	m_DX11Render->GetDeviceContext()->IASetInputLayout(m_pSceneVSLayout);

	m_DX11Render->GetDeviceContext()->VSSetShader(m_pSceneVertexShader, NULL, 0);
	m_DX11Render->GetDeviceContext()->PSSetShader(m_pScenePixelShader, NULL, 0);
	m_DX11Render->GetDeviceContext()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	//rendering scene
	for (auto& pActor : Actors)
	{
		hr = m_DX11Render->GetDeviceContext()->Map(m_pScenePixelShaderCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource);
		if (hr == S_OK)
		{
			CB_PS_PER_OBJECT* pPsPerObject = static_cast<CB_PS_PER_OBJECT*>(MappedResource.pData);
			pPsPerObject->m_fSpecExp = tempG1;
			pPsPerObject->m_fSpecIntensity = tempG2;
			pPsPerObject->m_bUseNormalMap = FALSE;//(pActor->pSRV_ary[1] != NULL);
			pPsPerObject->m_mWorld = PEMatrix::MakeTranspose(MatView);
			m_DX11Render->GetDeviceContext()->Unmap(m_pScenePixelShaderCB, 0);
			m_DX11Render->GetDeviceContext()->PSSetConstantBuffers(0, 1, &m_pScenePixelShaderCB);
		}

		pActor->Draw(m_DX11Render);
	}
}

void SceneManager::DoLighting()
{
	//Lighting Pass
	//ID3D11RenderTargetView* pRTV = m_DX11Render->GetRenderTargetView();
	m_DX11Render->GetDeviceContext()->OMSetRenderTargets(1, &m_HDRRTV, m_DepthStencilReadOnlyDSV);

	// Store the previous depth state
	ID3D11DepthStencilState* pPrevDepthState;
	UINT nPrevStencil;
	m_DX11Render->GetDSState(&pPrevDepthState, &nPrevStencil);
	m_DX11Render->GetDeviceContext()->PSSetSamplers(2, 1, &m_pPCFSampler);
	//prepare unpack info
	PrepareForLights();

	// render all kinds of Light
	DirectionLights();

	ID3D11BlendState* pPrevBlendState;
	FLOAT prevBlendFactor[4];
	UINT prevSampleMask;
	m_DX11Render->GetBlendState(&pPrevBlendState, prevBlendFactor, &prevSampleMask);
	m_DX11Render->SetBlendState(m_GeneralBS, prevBlendFactor, prevSampleMask);

	// Set the depth state for the rest of the lights
	m_DX11Render->SetDSState(m_NoDepthWriteGreatherStencilMaskState, 1);

	ID3D11RasterizerState* pPrevRSState;
	m_DX11Render->GetRSState(&pPrevRSState);
	m_DX11Render->SetRSState(m_NoDepthClipFrontRS);

	SpotLights();
	PointLights();
	

	// Restore the states
	m_DX11Render->SetBlendState(pPrevBlendState, prevBlendFactor, prevSampleMask);
	SAFE_RELEASE(pPrevBlendState);
	m_DX11Render->SetRSState(pPrevRSState);
	SAFE_RELEASE(pPrevRSState);

	m_DX11Render->SetDSState(pPrevDepthState, nPrevStencil);
	// Cleanup
	LightEnd();
}

void SceneManager::DebugGBuffer()
{
	ID3D11RenderTargetView* pRTV = m_DX11Render->GetRenderTargetView();
	m_DX11Render->GetDeviceContext()->OMSetRenderTargets(1, &pRTV, NULL);
	VisualizeGBuffer(m_DX11Render->GetDeviceContext());
	m_DX11Render->GetDeviceContext()->OMSetRenderTargets(1, &pRTV, m_DepthStencilDSV);
}

void SceneManager::AddActorsToScene(std::vector<OBJModel* >& objMods)
{
	for (const OBJModel* actor : objMods)
	{
		RenderActor* RA = new RenderActor();
		RA->Name = actor->Name;
		for (size_t i = 0; i < actor->FaceInfos.size(); ++i)
		{
			VertexLayOut_OBJ VLOB;
			VLOB.Pos = actor->VertexBuffer[actor->FaceInfos[i].VertexIdx];
			VLOB.Normal = actor->VertexNormal[actor->FaceInfos[i].NormalIdx];
			VLOB.UV = actor->UVs[actor->FaceInfos[i].UVIdx];
			VLOB.Tangent = actor->VertexTangent[actor->FaceInfos[i].VertexIdx];
			RA->VertexBuffer.push_back(VLOB);
		}
		RA->CreateVertexBuffer(m_DX11Render);
		RA->SetDiffuseTex(actor->DiffuseMapName.c_str(), m_DX11Render);
		if (actor->NormalMapName.empty())
		{
			RA->SetNormalTex(TEXT("..\\..\\PracticeEngine\\Media\\Texture\\DefaultNormalMap.bmp"), m_DX11Render);
		}
		else
			RA->SetNormalTex(actor->NormalMapName.c_str(), m_DX11Render);
		AddActor(RA);
	}
}

void SceneManager::DoDirectionLight(PEVector3& AmbientLower, PEVector3& AmbientUpper, DirectionalLight* DirLight)
{
	D3D11_MAPPED_SUBRESOURCE MappedResource;
	TRACE_HR(m_DX11Render->GetDeviceContext()->Map(m_pDirLightCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource));
	CB_DIRECTIONAL* pDirectionalValuesCB = (CB_DIRECTIONAL*)MappedResource.pData;
	pDirectionalValuesCB->vAmbientLower = GammaToLinear(AmbientLower);
	pDirectionalValuesCB->vAmbientRange = GammaToLinear(AmbientUpper) - GammaToLinear(AmbientLower);
	pDirectionalValuesCB->vDirToLight = -DirLight->GetLightDir();
	pDirectionalValuesCB->vDirectionalColor = GammaToLinear(DirLight->GetLightColor());
	const PEMatrix* w2s = m_CascadedMatrix.GetWorldToShadowSpace();
	pDirectionalValuesCB->ToShadowSpace = PEMatrix::MakeTranspose(*w2s);

	pDirectionalValuesCB->ToCascadeSpace[0] = m_CascadedMatrix.GetToCascadeOffsetX();
	pDirectionalValuesCB->ToCascadeSpace[1] = m_CascadedMatrix.GetToCascadeOffsetY();
	pDirectionalValuesCB->ToCascadeSpace[2] = m_CascadedMatrix.GetToCascadeScale();


	m_DX11Render->GetDeviceContext()->Unmap(m_pDirLightCB, 0);
	m_DX11Render->GetDeviceContext()->PSSetConstantBuffers(1, 1, &m_pDirLightCB);

	// Primitive settings
	m_DX11Render->GetDeviceContext()->IASetInputLayout(NULL);
	m_DX11Render->GetDeviceContext()->IASetVertexBuffers(0, 0, NULL, NULL, NULL);
	m_DX11Render->GetDeviceContext()->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);

	// Set the shaders
	m_DX11Render->GetDeviceContext()->VSSetShader(m_pDirLightVertexShader, NULL, 0);
	m_DX11Render->GetDeviceContext()->GSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->PSSetShader(m_pDirLightPixelShader, NULL, 0);

	m_DX11Render->GetDeviceContext()->PSSetShaderResources(4, 1, &m_pCascadedDepthStencilSRV[DirLight->m_LightIdx]);

	m_DX11Render->GetDeviceContext()->Draw(4, 0);

	ID3D11ShaderResourceView *arrRV[1] = { NULL };
	m_DX11Render->GetDeviceContext()->PSSetShaderResources(4, 1, arrRV);
	m_DX11Render->GetDeviceContext()->VSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->PSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}

void SceneManager::AddDirLight(DirectionalLight* DLight)
{
	DLight->m_LightIdx = m_DirectionalLights.size();
	m_DirectionalLights.push_back(DLight);
}

void SceneManager::AddPointLight(PointLight* PtLight)
{
	PtLight->m_LightIdx = m_PointLights.size();
	m_PointLights.push_back(PtLight);
}

void SceneManager::DirectionLights()
{
	for (auto DirLight : m_DirectionalLights)
	{
		DoDirectionLight(g_AmbientLow, g_AmbientUppder, DirLight);
	}
}

void SceneManager::PrepareForLights()
{
	PEMatrix MatView = *m_Camera->GetViewMatrix();
	PEMatrix MatProj = *m_Camera->GetProjectMatrix();
	PEMatrix mWVP = MatView * MatProj;

	D3D11_MAPPED_SUBRESOURCE MappedResource;
	TRACE_HR(m_DX11Render->GetDeviceContext()->Map(m_pGBufferUnpackCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource));
	CB_GBUFFER_UNPACK* pGBufferUnpackCB = (CB_GBUFFER_UNPACK*)MappedResource.pData;

	pGBufferUnpackCB->PerspectiveValues.x = 1.0f / MatProj.m[0][0];
	pGBufferUnpackCB->PerspectiveValues.y = 1.0f / MatProj.m[1][1];
	pGBufferUnpackCB->PerspectiveValues.z = MatProj.m[3][2];
	pGBufferUnpackCB->PerspectiveValues.w = -MatProj.m[2][2];
	PEMatrix matViewInv = PEMatrix::MakeInverse(MatView);
	pGBufferUnpackCB->ViewInv = PEMatrix::MakeTranspose(matViewInv);

	m_DX11Render->GetDeviceContext()->Unmap(m_pGBufferUnpackCB, 0);
	m_DX11Render->GetDeviceContext()->PSSetConstantBuffers(0, 1, &m_pGBufferUnpackCB);

	//Do Lighting
	m_DX11Render->GetDeviceContext()->OMSetDepthStencilState(m_NoDepthWriteLessStencilMaskState, 1);
	ID3D11ShaderResourceView* arrViews[4] = { m_DepthStencilSRV, m_ColorSpecIntensitySRV, m_NormalSRV, m_SpecPowerSRV };
	m_DX11Render->GetDeviceContext()->PSSetShaderResources(0, 4, arrViews);
}

void SceneManager::LightEnd()
{
	ID3D11ShaderResourceView* arrViews[4];
	ZeroMemory(arrViews, sizeof(arrViews));
	m_DX11Render->GetDeviceContext()->PSSetShaderResources(0, 4, arrViews);
	m_DX11Render->GetDeviceContext()->VSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->PSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->DSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->HSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}

void SceneManager::PointLights(bool bShowVolume)
{
	for (auto PtLight : m_PointLights)
	{
		DoPointLight(PtLight, bShowVolume);
	}
}

void SceneManager::DoPointLight(PointLight* PtLight, bool bShowVolume)
{

	float fRange = PtLight->GetLightRange();
	PEVector3 vPos = PtLight->GetLightPos();
	PEMatrix mLightWorldScale = PEMatrix::GetIdentityMatrix();
	mLightWorldScale.Scale(fRange, fRange, fRange);
	
	PEMatrix mLightWorldTrans = PEMatrix::GetIdentityMatrix();
	mLightWorldTrans.SetTranslation(vPos.x, vPos.y, vPos.z);
	
	PEMatrix mView = *m_Camera->GetViewMatrix();
	PEMatrix mProj = *m_Camera->GetProjectMatrix();
	PEMatrix mWorldViewProjection = mLightWorldScale * mLightWorldTrans * mView * mProj;

	D3D11_MAPPED_SUBRESOURCE MappedResource;
	TRACE_HR(m_DX11Render->GetDeviceContext()->Map(m_pPointLightDomainCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource));
	CB_POINT_LIGHT_DOMAIN* pPointLightDomainCB = (CB_POINT_LIGHT_DOMAIN*)MappedResource.pData;
	pPointLightDomainCB->WorldViewProj = PEMatrix::MakeTranspose(mWorldViewProjection);
	m_DX11Render->GetDeviceContext()->Unmap(m_pPointLightDomainCB, 0);
	m_DX11Render->GetDeviceContext()->DSSetConstantBuffers(0, 1, &m_pPointLightDomainCB);

	TRACE_HR(m_DX11Render->GetDeviceContext()->Map(m_pPointLightPixelCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource));
	CB_POINT_LIGHT_PIXEL* pPointLightPixelCB = (CB_POINT_LIGHT_PIXEL*)MappedResource.pData;
	pPointLightPixelCB->PointLightPos = vPos;
	pPointLightPixelCB->PointLightRangeRcp = 1.0f / fRange;
	pPointLightPixelCB->PointColor = GammaToLinear(PtLight->GetLightColor());
	pPointLightPixelCB->LightPerspectiveValues = PEFloat2(PtLight->m_ShadowMapViewProj.m[2][2], PtLight->m_ShadowMapViewProj.m[3][2]);

	m_DX11Render->GetDeviceContext()->Unmap(m_pPointLightPixelCB, 0);
	m_DX11Render->GetDeviceContext()->PSSetConstantBuffers(1, 1, &m_pPointLightPixelCB);

	m_DX11Render->GetDeviceContext()->PSSetShaderResources(4, 1, &m_PointDepthStencilSRV[PtLight->m_LightIdx]);

	m_DX11Render->GetDeviceContext()->IASetInputLayout(NULL);
	m_DX11Render->GetDeviceContext()->IASetVertexBuffers(0, 0, NULL, NULL, NULL);
	m_DX11Render->GetDeviceContext()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST);

	// Set the shaders
	m_DX11Render->GetDeviceContext()->VSSetShader(m_pPointLightVertexShader, NULL, 0);
	m_DX11Render->GetDeviceContext()->HSSetShader(m_pPointLightHullShader, NULL, 0);
	m_DX11Render->GetDeviceContext()->DSSetShader(m_pPointLightDomainShader, NULL, 0);
	m_DX11Render->GetDeviceContext()->GSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->PSSetShader(bShowVolume ? m_pDebugLightPixelShader : m_pPointLightPixelShader, NULL, 0);

	m_DX11Render->GetDeviceContext()->Draw(2, 0);

	ID3D11ShaderResourceView* nullSRV = NULL;
	m_DX11Render->GetDeviceContext()->PSSetShaderResources(4, 1, &nullSRV);
}

void SceneManager::DebugPointLightVolume()
{
	ID3D11RasterizerState* pPrevRSState;
	m_DX11Render->GetRSState(&pPrevRSState);
	m_DX11Render->SetRSState(m_WireFrameRS);

	PointLights(true);
	SpotLights(true);
	// Cleanup
	m_DX11Render->GetDeviceContext()->VSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->HSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->DSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->PSSetShader(NULL, NULL, 0);

	// Restore the states
	m_DX11Render->GetDeviceContext()->RSSetState(pPrevRSState);
	SAFE_RELEASE(pPrevRSState);
}

void SceneManager::InitGBuffer()
{
	D3D11_TEXTURE2D_DESC dtd = {
		m_DX11Render->GetBackBufferWidth(), //UINT Width;
		m_DX11Render->GetBackBufferHeight(), //UINT Height;
		1, //UINT MipLevels;
		1, //UINT ArraySize;
		DXGI_FORMAT_UNKNOWN, //DXGI_FORMAT Format;
		1, //DXGI_SAMPLE_DESC SampleDesc;
		0,
		D3D11_USAGE_DEFAULT,//D3D11_USAGE Usage;
		D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE,//UINT BindFlags;
		0,//UINT CPUAccessFlags;
		0//UINT MiscFlags;    
	};
	dtd.Format = depthStencilTextureFormat;
	TRACE_HR(m_DX11Render->GetDevice()->CreateTexture2D(&dtd, NULL, &m_DepthStencilRT));
	SetDebugObjectName(m_DepthStencilRT, "GBuffer - Depth Stencil");

	dtd.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
	dtd.Format = basicColorTextureFormat;
	TRACE_HR(m_DX11Render->GetDevice()->CreateTexture2D(&dtd, NULL, &m_ColorSpecIntensityRT));
	SetDebugObjectName(m_ColorSpecIntensityRT, "GBuffer - Base Color Specular Intensity");

	dtd.Format = normalTextureFormat;
	TRACE_HR(m_DX11Render->GetDevice()->CreateTexture2D(&dtd, NULL, &m_NormalRT));
	SetDebugObjectName(m_NormalRT, "GBuffer - Normal");

	dtd.Format = specPowTextureFormat;
	TRACE_HR(m_DX11Render->GetDevice()->CreateTexture2D(&dtd, NULL, &m_SpecPowerRT));
	SetDebugObjectName(m_SpecPowerRT, "GBuffer - Specular Power");

	D3D11_DEPTH_STENCIL_VIEW_DESC dsvd =
	{
		depthStencilRenderViewFormat,
		D3D11_DSV_DIMENSION_TEXTURE2D,
		0
	};

	TRACE_HR(m_DX11Render->CreateDSView(m_DepthStencilRT, &dsvd, &m_DepthStencilDSV));
	SetDebugObjectName(m_DepthStencilDSV, "GBuffer - Depth Stencil DSV");

	dsvd.Flags = D3D11_DSV_READ_ONLY_DEPTH | D3D11_DSV_READ_ONLY_STENCIL;
	TRACE_HR(m_DX11Render->CreateDSView(m_DepthStencilRT, &dsvd, &m_DepthStencilReadOnlyDSV));
	SetDebugObjectName(m_DepthStencilReadOnlyDSV, "GBuffer - Depth Stencil Read Only DSV");

	D3D11_RENDER_TARGET_VIEW_DESC rtsvd =
	{
		basicColorRenderViewFormat,
		D3D11_RTV_DIMENSION_TEXTURE2D
	};
	TRACE_HR(m_DX11Render->CreateRTView(m_ColorSpecIntensityRT, &rtsvd, &m_ColorSpecIntensityRTV));
	SetDebugObjectName(m_ColorSpecIntensityRTV, "GBuffer - Color Spec Intensity RTV");

	rtsvd.Format = normalRenderViewFormat;
	TRACE_HR(m_DX11Render->CreateRTView(m_NormalRT, &rtsvd, &m_NormalRTV));
	SetDebugObjectName(m_NormalRTV, "GBuffer - Normal RTV");

	rtsvd.Format = specPowRenderViewFormat;
	TRACE_HR(m_DX11Render->CreateRTView(m_SpecPowerRT, &rtsvd, &m_SpecPowerRTV));
	SetDebugObjectName(m_SpecPowerRTV, "GBuffer - Spec Power RTV");

	D3D11_SHADER_RESOURCE_VIEW_DESC dsrvd =
	{
		depthStencilResourceViewFormat,
		D3D11_SRV_DIMENSION_TEXTURE2D,
		0,
		0
	};
	dsrvd.Texture2D.MipLevels = 1;
	TRACE_HR(m_DX11Render->CreateSRView(m_DepthStencilRT, &dsrvd, &m_DepthStencilSRV));
	SetDebugObjectName(m_DepthStencilSRV, "GBuffer - Depth SRV");

	dsrvd.Format = basicColorResourceViewFormat;
	TRACE_HR(m_DX11Render->CreateSRView(m_ColorSpecIntensityRT, &dsrvd, &m_ColorSpecIntensitySRV));
	SetDebugObjectName(m_ColorSpecIntensitySRV, "GBuffer - Color Spec Intensity SRV");

	dsrvd.Format = normalResourceViewFormat;
	TRACE_HR(m_DX11Render->CreateSRView(m_NormalRT, &dsrvd, &m_NormalSRV));
	SetDebugObjectName(m_NormalSRV, "GBuffer - Normal SRV");

	dsrvd.Format = specPowResourceViewFormat;
	TRACE_HR(m_DX11Render->CreateSRView(m_SpecPowerRT, &dsrvd, &m_SpecPowerSRV));
	SetDebugObjectName(m_SpecPowerSRV, "GBuffer - Spec Power SRV");

	D3D11_DEPTH_STENCIL_DESC descDepth;
	descDepth.DepthEnable = TRUE;
	descDepth.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
	descDepth.DepthFunc = D3D11_COMPARISON_LESS;
	descDepth.StencilEnable = TRUE;
	descDepth.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;
	descDepth.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
	D3D11_DEPTH_STENCILOP_DESC stencilMarkOp = { D3D11_STENCIL_OP_REPLACE, D3D11_STENCIL_OP_REPLACE, D3D11_STENCIL_OP_REPLACE, D3D11_COMPARISON_ALWAYS };
	descDepth.FrontFace = stencilMarkOp;
	descDepth.BackFace = stencilMarkOp;
	TRACE_HR(m_DX11Render->GetDevice()->CreateDepthStencilState(&descDepth, &m_DepthStencilState));
	SetDebugObjectName(m_DepthStencilState, "GBuffer - Depth Stencil Mark DS");

	descDepth.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
	stencilMarkOp = { D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_COMPARISON_EQUAL };
	descDepth.FrontFace = stencilMarkOp;
	descDepth.BackFace = stencilMarkOp;
	TRACE_HR(m_DX11Render->GetDevice()->CreateDepthStencilState(&descDepth, &m_NoDepthWriteLessStencilMaskState));
	SetDebugObjectName(m_NoDepthWriteLessStencilMaskState, "GBuffer - Depth Test Less / No Write, Stencil Mask DS");

	descDepth.DepthFunc = D3D11_COMPARISON_GREATER_EQUAL;
	TRACE_HR(m_DX11Render->GetDevice()->CreateDepthStencilState(&descDepth, &m_NoDepthWriteGreatherStencilMaskState));
	SetDebugObjectName(m_NoDepthWriteGreatherStencilMaskState, "GBuffer - Depth Test Great / No Write, Stencil Mask DS");

	D3D11_BUFFER_DESC cbDesc;
	ZeroMemory(&cbDesc, sizeof(cbDesc));
	cbDesc.Usage = D3D11_USAGE_DYNAMIC;
	cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	cbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	cbDesc.ByteWidth = sizeof(CB_GBUFFER_UNPACK);
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&cbDesc, NULL, &m_pGBufferUnpackCB));
	SetDebugObjectName(m_pGBufferUnpackCB, "GBufferUnpack CB");

	cbDesc.ByteWidth = sizeof(CB_DIRECTIONAL);
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&cbDesc, NULL, &m_pDirLightCB));
	SetDebugObjectName(m_pDirLightCB, "Directional Light CB");

	D3D11_SAMPLER_DESC samDesc;
	ZeroMemory(&samDesc, sizeof(samDesc));
	samDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
	samDesc.AddressU = samDesc.AddressV = samDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	samDesc.MaxAnisotropy = 1;
	samDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
	samDesc.MaxLOD = D3D11_FLOAT32_MAX;
	TRACE_HR(m_DX11Render->GetDevice()->CreateSamplerState(&samDesc, &m_pSampLinear));
	SetDebugObjectName(m_pSampLinear, "Linear Sampler");

	samDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
	TRACE_HR(m_DX11Render->GetDevice()->CreateSamplerState(&samDesc, &m_pSampPoint));
	SetDebugObjectName(m_pSampPoint, "Point Sampler");

	
	ZeroMemory(&samDesc, sizeof(samDesc));
	samDesc.Filter = D3D11_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR;
	samDesc.AddressU = samDesc.AddressV = samDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
	samDesc.MaxAnisotropy = 1;
	samDesc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL;
	samDesc.MaxLOD = D3D11_FLOAT32_MAX;
	TRACE_HR(m_DX11Render->GetDevice()->CreateSamplerState(&samDesc, &m_pPCFSampler));
	SetDebugObjectName(m_pPCFSampler, "PCF Sampler");

	dtd.Format = DXGI_FORMAT_R16G16B16A16_TYPELESS;
	dtd.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
	TRACE_HR(m_DX11Render->GetDevice()->CreateTexture2D(&dtd, NULL, &m_pHDRTexture));
	SetDebugObjectName(m_pHDRTexture, "HDR RenderTarget");

	rtsvd =
	{
		DXGI_FORMAT_R16G16B16A16_FLOAT,
		D3D11_RTV_DIMENSION_TEXTURE2D
	};
	TRACE_HR(m_DX11Render->GetDevice()->CreateRenderTargetView(m_pHDRTexture, &rtsvd, &m_HDRRTV));
	SetDebugObjectName(m_HDRRTV, "HDR Light Accumulation RTV");

	dsrvd.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
	dsrvd.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;

	dsrvd.Texture2D.MipLevels = 1;
	TRACE_HR(m_DX11Render->GetDevice()->CreateShaderResourceView(m_pHDRTexture, &dsrvd, &m_HDRSRV));
	SetDebugObjectName(m_HDRSRV, "HDR Light Accumulation SRV");

	//////////////////////////////////////

	dtd.Width = m_DX11Render->GetBackBufferWidth() / 4;
	dtd.Height = m_DX11Render->GetBackBufferHeight() / 4;
	dtd.Format = DXGI_FORMAT_R16G16B16A16_TYPELESS;
	dtd.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS;
	TRACE_HR(m_DX11Render->GetDevice()->CreateTexture2D(&dtd, NULL, &m_pDownScaleRT));
	SetDebugObjectName(m_pDownScaleRT, "PostFX - Down Scaled RT");

	// Create the resource views
	ZeroMemory(&dsrvd, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
	dsrvd.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
	dsrvd.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	dsrvd.Texture2D.MipLevels = 1;
	TRACE_HR(m_DX11Render->GetDevice()->CreateShaderResourceView(m_pDownScaleRT, &dsrvd, &m_pDownScaleSRV));
	SetDebugObjectName(m_pDownScaleSRV, "PostFX - Down Scaled SRV");

	// Create the UAVs
	D3D11_UNORDERED_ACCESS_VIEW_DESC DescUAV;
	ZeroMemory(&DescUAV, sizeof(D3D11_UNORDERED_ACCESS_VIEW_DESC));
	DescUAV.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
	DescUAV.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
	DescUAV.Buffer.FirstElement = 0;
	DescUAV.Buffer.NumElements = m_DX11Render->GetBackBufferWidth() * m_DX11Render->GetBackBufferHeight() / 16;
	TRACE_HR(m_DX11Render->GetDevice()->CreateUnorderedAccessView(m_pDownScaleRT, &DescUAV, &m_pDownScaleUAV));
	SetDebugObjectName(m_pDownScaleUAV, "PostFX - Down Scaled UAV");

	//////////////////////////////////////////////////////////////////////////////////////////////////////
	// Allocate temporary target
	TRACE_HR(m_DX11Render->GetDevice()->CreateTexture2D(&dtd, NULL, &m_pTempRT[0]));
	SetDebugObjectName(m_pTempRT[0], "PostFX - Temp 0 RT");

	TRACE_HR(m_DX11Render->GetDevice()->CreateShaderResourceView(m_pTempRT[0], &dsrvd, &m_pTempSRV[0]));
	SetDebugObjectName(m_pTempSRV[0], "PostFX - Temp 0 SRV");

	TRACE_HR(m_DX11Render->GetDevice()->CreateUnorderedAccessView(m_pTempRT[0], &DescUAV, &m_pTempUAV[0]));
	SetDebugObjectName(m_pTempUAV[0], "PostFX - Temp 0 UAV");

	TRACE_HR(m_DX11Render->GetDevice()->CreateTexture2D(&dtd, NULL, &m_pTempRT[1]));
	SetDebugObjectName(m_pTempRT[1], "PostFX - Temp 1 RT");

	TRACE_HR(m_DX11Render->GetDevice()->CreateShaderResourceView(m_pTempRT[1], &dsrvd, &m_pTempSRV[1]));
	SetDebugObjectName(m_pTempSRV[1], "PostFX - Temp 1 SRV");

	TRACE_HR(m_DX11Render->GetDevice()->CreateUnorderedAccessView(m_pTempRT[1], &DescUAV, &m_pTempUAV[1]));
	SetDebugObjectName(m_pTempUAV[1], "PostFX - Temp 1 UAV");

	//////////////////////////////////////////////////////////////////////////////////////////////////////
	// Allocate bloom target
	TRACE_HR(m_DX11Render->GetDevice()->CreateTexture2D(&dtd, NULL, &m_pBloomRT));
	SetDebugObjectName(m_pBloomRT, "PostFX - Bloom RT");

	TRACE_HR(m_DX11Render->GetDevice()->CreateShaderResourceView(m_pBloomRT, &dsrvd, &m_pBloomSRV));
	SetDebugObjectName(m_pBloomSRV, "PostFX - Bloom SRV");

	TRACE_HR(m_DX11Render->GetDevice()->CreateUnorderedAccessView(m_pBloomRT, &DescUAV, &m_pBloomUAV));
	SetDebugObjectName(m_pBloomUAV, "PostFX - Bloom UAV");
	/////////////////////////////////////

	//////////////SSAO
	dtd.Width = m_DX11Render->GetBackBufferWidth() / 2;
	dtd.Height = m_DX11Render->GetBackBufferHeight() / 2;
	dtd.Format = DXGI_FORMAT_R32_TYPELESS;
	dtd.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS;
	TRACE_HR(m_DX11Render->GetDevice()->CreateTexture2D(&dtd, NULL, &m_pSSAO_RT));
	SetDebugObjectName(m_pSSAO_RT, "SSAO - Final AO values");

	ZeroMemory(&DescUAV, sizeof(DescUAV));
	DescUAV.Format = DXGI_FORMAT_R32_FLOAT;
	DescUAV.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
	TRACE_HR(m_DX11Render->GetDevice()->CreateUnorderedAccessView(m_pSSAO_RT, &DescUAV, &m_pSSAO_UAV));
	SetDebugObjectName(m_pSSAO_UAV, "SSAO - Final AO values UAV");

	ZeroMemory(&dsrvd, sizeof(dsrvd));
	dsrvd.Format = DXGI_FORMAT_R32_FLOAT;
	dsrvd.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	dsrvd.Texture2D.MipLevels = 1;
	TRACE_HR(m_DX11Render->GetDevice()->CreateShaderResourceView(m_pSSAO_RT, &dsrvd, &m_pSSAO_SRV));
	SetDebugObjectName(m_pSSAO_SRV, "PostFX - Bloom SRV");

	ZeroMemory(&cbDesc, sizeof(cbDesc));
	cbDesc.Usage = D3D11_USAGE_DYNAMIC;
	cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	cbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	cbDesc.ByteWidth = sizeof(CB_SSAO_DOWN_SCALE);
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&cbDesc, NULL, &m_pSSAODownscaleCB));
	SetDebugObjectName(m_pSSAODownscaleCB, "SSAO - Downscale Depth CB");

	///////////////////

	D3D11_BUFFER_DESC bufferDesc;
	ZeroMemory(&bufferDesc, sizeof(bufferDesc));
	bufferDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
	bufferDesc.StructureByteStride = sizeof(float);
	bufferDesc.ByteWidth = m_nDownScaleGroups * bufferDesc.StructureByteStride;
	bufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&bufferDesc, NULL, &m_pDownScale1DBuffer));
	SetDebugObjectName(m_pDownScale1DBuffer, "PostFX - Down Scale 1D Buffer");

	
	ZeroMemory(&DescUAV, sizeof(D3D11_UNORDERED_ACCESS_VIEW_DESC));
	DescUAV.Format = DXGI_FORMAT_UNKNOWN;
	DescUAV.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
	DescUAV.Buffer.NumElements = m_nDownScaleGroups;
	TRACE_HR(m_DX11Render->GetDevice()->CreateUnorderedAccessView(m_pDownScale1DBuffer, &DescUAV, &m_pDownScale1DUAV));
	SetDebugObjectName(m_pDownScale1DUAV, "PostFX - Luminance Down Scale 1D UAV");

	ZeroMemory(&dsrvd, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
	dsrvd.Format = DXGI_FORMAT_UNKNOWN;
	dsrvd.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
	dsrvd.Buffer.NumElements = m_nDownScaleGroups;
	TRACE_HR(m_DX11Render->GetDevice()->CreateShaderResourceView(m_pDownScale1DBuffer, &dsrvd, &m_pDownScale1DSRV));
	SetDebugObjectName(m_pDownScale1DSRV, "PostFX - Down Scale 1D SRV");

	bufferDesc.ByteWidth = sizeof(float);
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&bufferDesc, NULL, &m_pAvgLumBuffer));
	SetDebugObjectName(m_pAvgLumBuffer, "PostFX - Average Luminance Buffer");
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&bufferDesc, NULL, &m_pPrevAvgLumBuffer));
	SetDebugObjectName(m_pPrevAvgLumBuffer, "PostFX - Previous Average Luminance Buffer");

	DescUAV.Buffer.NumElements = 1;
	TRACE_HR(m_DX11Render->GetDevice()->CreateUnorderedAccessView(m_pAvgLumBuffer, &DescUAV, &m_pAvgLumUAV));
	SetDebugObjectName(m_pAvgLumUAV, "PostFX - Average Luminance UAV");
	TRACE_HR(m_DX11Render->GetDevice()->CreateUnorderedAccessView(m_pPrevAvgLumBuffer, &DescUAV, &m_pPrevAvgLumUAV));
	SetDebugObjectName(m_pPrevAvgLumUAV, "PostFX - Previous Average Luminance UAV");

	dsrvd.Buffer.NumElements = 1;
	TRACE_HR(m_DX11Render->GetDevice()->CreateShaderResourceView(m_pAvgLumBuffer, &dsrvd, &m_pAvgLumSRV));
	SetDebugObjectName(m_pAvgLumSRV, "PostFX - Average Luminance SRV");
	TRACE_HR(m_DX11Render->GetDevice()->CreateShaderResourceView(m_pPrevAvgLumBuffer, &dsrvd, &m_pPrevAvgLumSRV));
	SetDebugObjectName(m_pPrevAvgLumSRV, "PostFX - Previous Average Luminance SRV");

	/////////////////////////
	int SSAO_WxH = m_DX11Render->GetBackBufferWidth() / 2 * m_DX11Render->GetBackBufferHeight() / 2;
	ZeroMemory(&bufferDesc, sizeof(bufferDesc));
	bufferDesc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
	bufferDesc.StructureByteStride = 4 * sizeof(float);
	bufferDesc.ByteWidth = SSAO_WxH * bufferDesc.StructureByteStride;
	bufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
	TRACE_HR(m_DX11Render->GetDevice()->CreateBuffer(&bufferDesc, NULL, &m_pMiniDepthBuffer));
	SetDebugObjectName(m_pMiniDepthBuffer, "SSAO - Downscaled Depth Buffer");

	ZeroMemory(&DescUAV, sizeof(DescUAV));
	DescUAV.Format = DXGI_FORMAT_UNKNOWN;
	DescUAV.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
	DescUAV.Buffer.FirstElement = 0;
	DescUAV.Buffer.NumElements = SSAO_WxH;
	TRACE_HR(m_DX11Render->GetDevice()->CreateUnorderedAccessView(m_pMiniDepthBuffer, &DescUAV, &m_pMiniDepthUAV));
	SetDebugObjectName(m_pMiniDepthUAV, "SSAO - Downscaled Depth UAV");

	ZeroMemory(&dsrvd, sizeof(dsrvd));
	dsrvd.Format = DXGI_FORMAT_UNKNOWN;
	dsrvd.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
	dsrvd.Buffer.FirstElement = 0;
	dsrvd.Buffer.NumElements = SSAO_WxH;
	TRACE_HR(m_DX11Render->GetDevice()->CreateShaderResourceView(m_pMiniDepthBuffer, &dsrvd, &m_pMiniDepthSRV));
	SetDebugObjectName(m_pMiniDepthSRV, "SSAO - Downscaled Depth SRV");
	////////////////////////
}

void SceneManager::PostRendering()
{
	m_DX11Render->GetDeviceContext()->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	// Little cleanup
	ID3D11RenderTargetView* rt[3] = { NULL, NULL, NULL };
	m_DX11Render->GetDeviceContext()->OMSetRenderTargets(3, rt, m_DepthStencilReadOnlyDSV);
}

void SceneManager::PreRendering()
{
	ID3D11SamplerState* samplers[2] = { m_pSampLinear, m_pSampPoint };
	m_DX11Render->GetDeviceContext()->PSSetSamplers(0, 2, samplers);
	//m_DX11Render->PSSetSampler(&m_pSampLinear);
	m_DX11Render->GetDeviceContext()->ClearDepthStencilView(m_DepthStencilDSV, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0, 0);

	float ClearColor[4] = { .0f, .0f, .0f, .0f };
	m_DX11Render->GetDeviceContext()->ClearRenderTargetView(m_ColorSpecIntensityRTV, ClearColor);
	m_DX11Render->GetDeviceContext()->ClearRenderTargetView(m_NormalRTV, ClearColor);
	m_DX11Render->GetDeviceContext()->ClearRenderTargetView(m_SpecPowerRTV, ClearColor);

	ID3D11RenderTargetView* rt[3] = { m_ColorSpecIntensityRTV, m_NormalRTV, m_SpecPowerRTV };
	m_DX11Render->GetDeviceContext()->OMSetRenderTargets(3, rt, m_DepthStencilDSV);

	m_DX11Render->GetDeviceContext()->OMSetDepthStencilState(m_DepthStencilState, 1);
}

void SceneManager::SpotLights(bool bShowVolume /*= false*/)
{
	for (auto SptLight : m_SpotLights)
	{
		DoSpotLight(SptLight, bShowVolume);
	}
}

void SceneManager::DoSpotLight(SpotLight* SptLight, bool bShowVolume)
{
	float fCosInnerAngle = cosf(SptLight->GetInnerAngle());
	float fSinOuterAngle = sinf(SptLight->GetOuterAngle());
	float fCosOuterAngle = cosf(SptLight->GetOuterAngle());

	float fRange = SptLight->GetLightRange();
	// Scale matrix from the cone local space to the world angles and range
	PEMatrix mLightWorldScale = PEMatrix::GetIdentityMatrix();
	mLightWorldScale.Scale(fRange, fRange, fRange);
	PEVector3 vDir = SptLight->GetLightDir();
	// Rotate and translate matrix from cone local space to lights world space
	PEVector3 vUp = (vDir.y > 0.9 || vDir.y < -0.9) ? PEVector3(0.0f, 0.0f, vDir.y) : PEVector3(0.0f, 1.0f, 0.0f);
	PEVector3 vRight = vUp.Cross(vDir);
	vRight.Normalize();
	vUp = vDir.Cross(vRight);
	vUp.Normalize();
	PEVector3 vPos = SptLight->GetLightPos();
	PEVector3 vAt = vPos + vDir * fRange;
	PEMatrix m_LightWorldTransRotate = PEMatrix::GetIdentityMatrix();
	
	for (int i = 0; i < 3; i++)
	{
		m_LightWorldTransRotate.m[0][i] = (&vRight.x)[i];
		m_LightWorldTransRotate.m[1][i] = (&vUp.x)[i];
		m_LightWorldTransRotate.m[2][i] = (&vDir.x)[i];
		m_LightWorldTransRotate.m[3][i] = (&vPos.x)[i];
	}

	// Prepare the combined local to projected space matrix
	PEMatrix mView = *m_Camera->GetViewMatrix();
	PEMatrix mProj = *m_Camera->GetProjectMatrix();
	PEMatrix mWorldViewProjection = mLightWorldScale * m_LightWorldTransRotate * mView * mProj;

	// Write the matrix to the domain shader constant buffer
	D3D11_MAPPED_SUBRESOURCE MappedResource;
	TRACE_HR(m_DX11Render->GetDeviceContext()->Map(m_pSpotLightDomainCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource));
	CB_SPOT_LIGHT_DOMAIN* pSpotLightDomainCB = (CB_SPOT_LIGHT_DOMAIN*)MappedResource.pData;
	pSpotLightDomainCB->WolrdViewProj = PEMatrix::MakeTranspose(mWorldViewProjection);
	pSpotLightDomainCB->fSinAngle = fSinOuterAngle;
	pSpotLightDomainCB->fCosAngle = fCosOuterAngle;
	m_DX11Render->GetDeviceContext()->Unmap(m_pSpotLightDomainCB, 0);
	m_DX11Render->GetDeviceContext()->DSSetConstantBuffers(0, 1, &m_pSpotLightDomainCB);

	TRACE_HR(m_DX11Render->GetDeviceContext()->Map(m_pSpotLightPixelCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource));
	CB_SPOT_LIGHT_PIXEL* pSpotLightPixelCB = (CB_SPOT_LIGHT_PIXEL*)MappedResource.pData;
	pSpotLightPixelCB->SpotLightPos = vPos;
	pSpotLightPixelCB->SpotLightRangeRcp = 1.0f / fRange;
	pSpotLightPixelCB->vDirToLight = -vDir;
	pSpotLightPixelCB->SpotCosOuterCone = fCosOuterAngle;
	pSpotLightPixelCB->SpotColor = GammaToLinear(SptLight->GetLightColor());
	pSpotLightPixelCB->SpotCosConeAttRange = fCosInnerAngle - fCosOuterAngle;
	pSpotLightPixelCB->ToShadowMap = PEMatrix::MakeTranspose(SptLight->m_ShadowMapViewProj);
	m_DX11Render->GetDeviceContext()->Unmap(m_pSpotLightPixelCB, 0);
	m_DX11Render->GetDeviceContext()->PSSetConstantBuffers(1, 1, &m_pSpotLightPixelCB);

	m_DX11Render->GetDeviceContext()->PSSetShaderResources(4, 1, &m_SpotDepthStencilSRV[SptLight->m_LightIdx]);

	m_DX11Render->GetDeviceContext()->IASetInputLayout(NULL);
	m_DX11Render->GetDeviceContext()->IASetVertexBuffers(0, 0, NULL, NULL, NULL);
	m_DX11Render->GetDeviceContext()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST);

	// Set the shaders
	m_DX11Render->GetDeviceContext()->VSSetShader(m_pSpotLightVertexShader, NULL, 0);
	m_DX11Render->GetDeviceContext()->HSSetShader(m_pSpotLightHullShader, NULL, 0);
	m_DX11Render->GetDeviceContext()->DSSetShader(m_pSpotLightDomainShader, NULL, 0);
	m_DX11Render->GetDeviceContext()->GSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->PSSetShader(bShowVolume ? m_pDebugLightPixelShader : m_pSpotLightPixelShader, NULL, 0);

	m_DX11Render->GetDeviceContext()->Draw(1, 0);

	ID3D11ShaderResourceView* nullSRV = NULL;
	m_DX11Render->GetDeviceContext()->PSSetShaderResources(4, 1, &nullSRV);
}

void SceneManager::AddSpotLight(SpotLight* SptLight)
{
	SptLight->m_LightIdx = m_SpotLights.size();
	m_SpotLights.push_back(SptLight);
}

void SceneManager::GenerateShadowMap()
{
	D3D11_VIEWPORT oldvp;
	UINT num = 1;
	m_DX11Render->GetDeviceContext()->RSGetViewports(&num, &oldvp);
	ID3D11RasterizerState* pPrevRSState;
	m_DX11Render->GetDeviceContext()->RSGetState(&pPrevRSState);

	GenSpotLightShadowMap();
	
	GenPointLightShadowMap();

	m_DX11Render->GetDeviceContext()->RSSetState(m_pCascadedShadowGenRS);
	GenCascadeShadowMap();

	m_DX11Render->GetDeviceContext()->RSSetViewports(num, &oldvp);
	m_DX11Render->GetDeviceContext()->RSSetState(pPrevRSState);
	SAFE_RELEASE(pPrevRSState);
}

void SceneManager::GenSpotLightShadowMap()
{
	
	static D3D11_TEXTURE2D_DESC dtd = {
		SHADOWMAP_SIZE_SPOT, //UINT Width;
		SHADOWMAP_SIZE_SPOT, //UINT Height;
		1, //UINT MipLevels;
		1, //UINT ArraySize;
		//DXGI_FORMAT_R24G8_TYPELESS,
		DXGI_FORMAT_R32_TYPELESS, //DXGI_FORMAT Format;
		1, //DXGI_SAMPLE_DESC SampleDesc;
		0,
		D3D11_USAGE_DEFAULT,//D3D11_USAGE Usage;
		D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE,//UINT BindFlags;
		0,//UINT CPUAccessFlags;
		0//UINT MiscFlags;    
	};
	static D3D11_DEPTH_STENCIL_VIEW_DESC descDepthView =
	{
		// DXGI_FORMAT_D24_UNORM_S8_UINT,
		DXGI_FORMAT_D32_FLOAT,
		D3D11_DSV_DIMENSION_TEXTURE2D,
		0
	};
	static D3D11_SHADER_RESOURCE_VIEW_DESC descShaderView =
	{
		//DXGI_FORMAT_R24_UNORM_X8_TYPELESS,
		DXGI_FORMAT_R32_FLOAT,
		D3D11_SRV_DIMENSION_TEXTURE2D,
		0,
		0
	};
	descShaderView.Texture2D.MipLevels = 1;

	int cnt = m_SpotLights.size() - m_SpotDepthStencilDSV.size();
	if (cnt > 0)
	{
		m_SpotDepthStencilDSV.resize(cnt);
		m_SpotDepthStencilRT.resize(cnt);
		m_SpotDepthStencilSRV.resize(cnt);
		int nowCnt = m_SpotLights.size();
		char strResName[32];
		for (unsigned int i = cnt; i > 0; --i)
		{
			int label = nowCnt - i;

			sprintf_s(strResName, "Spot Shado wmap Target %d", label);
			TRACE_HR(m_DX11Render->CreateTexture2D(&dtd, NULL, &m_SpotDepthStencilRT[label]));
			SetDebugObjectName(m_SpotDepthStencilRT[label], strResName);

			sprintf_s(strResName, "Spot Shado wmap Resource View %d", label);
			TRACE_HR(m_DX11Render->CreateSRView(m_SpotDepthStencilRT[label], &descShaderView, &m_SpotDepthStencilSRV[label]));
			SetDebugObjectName(m_SpotDepthStencilSRV[label], strResName);

			sprintf_s(strResName, "Spot Shado wmap Depth View %d", label);
			TRACE_HR(m_DX11Render->CreateDSView(m_SpotDepthStencilRT[label], &descDepthView, &m_SpotDepthStencilDSV[label]));
			SetDebugObjectName(m_SpotDepthStencilDSV[label], strResName);
		}
	}

	D3D11_VIEWPORT vp[1] = { { 0, 0, SHADOWMAP_SIZE_SPOT, SHADOWMAP_SIZE_SPOT, 0.0f, 1.0f } };
	m_DX11Render->GetDeviceContext()->RSSetViewports(1, vp);

	m_DX11Render->GetDeviceContext()->RSSetState(m_pShadowGenRS);
	// Set the vertex layout
	m_DX11Render->GetDeviceContext()->IASetInputLayout(m_pShadowGenVSLayout);
	m_DX11Render->GetDeviceContext()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	// Set the shadow generation shaders
	m_DX11Render->GetDeviceContext()->VSSetShader(m_pSpotShadowGenVertexShader, NULL, 0);
	m_DX11Render->GetDeviceContext()->GSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->PSSetShader(NULL, NULL, 0);

	for (unsigned int i = 0; i < m_SpotLights.size(); ++i)
	{
		// Set the depth target
		ID3D11RenderTargetView* nullRT = NULL;
		ID3D11DepthStencilView* pDSV = m_SpotDepthStencilDSV[i];
		m_DX11Render->GetDeviceContext()->OMSetRenderTargets(1, &nullRT, pDSV);
		m_DX11Render->GetDeviceContext()->ClearDepthStencilView(pDSV, D3D11_CLEAR_DEPTH, 1.0, 0);

		PEVector3 vPos = m_SpotLights[i]->GetLightPos();
		PEVector3 LtDir = m_SpotLights[i]->GetLightDir();
		PEVector3 vLookAt = vPos + LtDir * m_SpotLights[i]->GetLightRange();
		PEVector3 vUp = (LtDir.y > 0.9 || LtDir.y < -0.9) ? PEVector3(.0f, .0f, LtDir.y) : PEVector3(.0f, 1.0f, .0f);
		PEVector3 vRight = vUp.Cross(LtDir);
		vRight.Normalize();
		vUp = LtDir.Cross(vRight);
		vUp.Normalize();

		PEMatrix matSpotView = PEMatrix::MakeLookAtLHMatrix(&vPos, &vLookAt, &vUp);
		PEMatrix matSpotProj = PEMatrix::MakePerspectiveMatrix(2.0f*m_SpotLights[i]->GetOuterAngle(), 1.0f, SHADOWMAP_NEAR, m_SpotLights[i]->GetLightRange());

		D3D11_MAPPED_SUBRESOURCE MappedResource;
		m_DX11Render->GetDeviceContext()->Map(m_pSpotShadowGenVertexCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource);
		PEMatrix* pShadowGenMat = (PEMatrix*)MappedResource.pData;
		m_SpotLights[i]->m_ShadowMapViewProj = matSpotView * matSpotProj;
		*pShadowGenMat = PEMatrix::MakeTranspose(m_SpotLights[i]->m_ShadowMapViewProj);
		m_DX11Render->GetDeviceContext()->Unmap(m_pSpotShadowGenVertexCB, 0);
		m_DX11Render->GetDeviceContext()->VSSetConstantBuffers(0, 1, &m_pSpotShadowGenVertexCB);

		for (auto& pActor : Actors)
		{
			pActor->Draw(m_DX11Render);
		}
		/*pDSV = NULL;
		m_DX11Render->GetDeviceContext()->OMSetRenderTargets(1, &nullRT, pDSV);*/
	}

	// Cleanup
	m_DX11Render->GetDeviceContext()->VSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->GSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->PSSetShader(NULL, NULL, 0);
}

void SceneManager::GenPointLightShadowMap()
{
	static D3D11_TEXTURE2D_DESC dtd = {
		SHADOWMAP_SIZE_SPOT, //UINT Width;
		SHADOWMAP_SIZE_SPOT, //UINT Height;
		1, //UINT MipLevels;
		6, //UINT ArraySize;
		//DXGI_FORMAT_R24G8_TYPELESS,
		DXGI_FORMAT_R32_TYPELESS, //DXGI_FORMAT Format;
		1, //DXGI_SAMPLE_DESC SampleDesc;
		0,
		D3D11_USAGE_DEFAULT,//D3D11_USAGE Usage;
		D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE,//UINT BindFlags;
		0,//UINT CPUAccessFlags;
		D3D11_RESOURCE_MISC_TEXTURECUBE//UINT MiscFlags;    
	};
	static D3D11_DEPTH_STENCIL_VIEW_DESC descDepthView =
	{
		// DXGI_FORMAT_D24_UNORM_S8_UINT,
		DXGI_FORMAT_D32_FLOAT,
		D3D11_DSV_DIMENSION_TEXTURE2DARRAY,
		0
	};
	descDepthView.Texture2DArray.FirstArraySlice = 0;
	descDepthView.Texture2DArray.ArraySize = 6;
	static D3D11_SHADER_RESOURCE_VIEW_DESC descShaderView =
	{
		//DXGI_FORMAT_R24_UNORM_X8_TYPELESS,
		DXGI_FORMAT_R32_FLOAT,
		D3D11_SRV_DIMENSION_TEXTURECUBE,
		0,
		0
	};
	descShaderView.TextureCube.MipLevels = 1;
	descShaderView.TextureCube.MostDetailedMip = 0;
	descShaderView.Texture2D.MipLevels = 1;

	int cnt = m_PointLights.size() - m_PointDepthStencilDSV.size();
	if (cnt > 0)
	{
		m_PointDepthStencilDSV.resize(cnt);
		m_PointDepthStencilRT.resize(cnt);
		m_PointDepthStencilSRV.resize(cnt);
		int nowCnt = m_PointLights.size();
		char strResName[32];
		for (unsigned int i = cnt; i > 0; --i)
		{
			int label = nowCnt - i;

			sprintf_s(strResName, "Point Shadow map Target %d", label);
			TRACE_HR(m_DX11Render->CreateTexture2D(&dtd, NULL, &m_PointDepthStencilRT[label]));
			SetDebugObjectName(m_PointDepthStencilRT[label], strResName);

			sprintf_s(strResName, "Point Shadow map Resource View %d", label);
			TRACE_HR(m_DX11Render->CreateSRView(m_PointDepthStencilRT[label], &descShaderView, &m_PointDepthStencilSRV[label]));
			SetDebugObjectName(m_PointDepthStencilSRV[label], strResName);

			sprintf_s(strResName, "Point Shadow map Depth View %d", label);
			TRACE_HR(m_DX11Render->CreateDSView(m_PointDepthStencilRT[label], &descDepthView, &m_PointDepthStencilDSV[label]));
			SetDebugObjectName(m_PointDepthStencilDSV[label], strResName);
		}
	}



	D3D11_VIEWPORT vp[6] = { 
			{ 0, 0, SHADOWMAP_SIZE_SPOT, SHADOWMAP_SIZE_SPOT, 0.0f, 1.0f }, 
			{ 0, 0, SHADOWMAP_SIZE_SPOT, SHADOWMAP_SIZE_SPOT, 0.0f, 1.0f }, 
			{ 0, 0, SHADOWMAP_SIZE_SPOT, SHADOWMAP_SIZE_SPOT, 0.0f, 1.0f }, 
			{ 0, 0, SHADOWMAP_SIZE_SPOT, SHADOWMAP_SIZE_SPOT, 0.0f, 1.0f }, 
			{ 0, 0, SHADOWMAP_SIZE_SPOT, SHADOWMAP_SIZE_SPOT, 0.0f, 1.0f }, 
			{ 0, 0, SHADOWMAP_SIZE_SPOT, SHADOWMAP_SIZE_SPOT, 0.0f, 1.0f } 
	};
	m_DX11Render->GetDeviceContext()->RSSetViewports(6, vp);
	m_DX11Render->GetDeviceContext()->RSSetState(m_pShadowGenRS);
	// Set the vertex layout
	m_DX11Render->GetDeviceContext()->IASetInputLayout(m_pShadowGenVSLayout);
	m_DX11Render->GetDeviceContext()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	// Set the shadow generation shaders
	m_DX11Render->GetDeviceContext()->VSSetShader(m_pPointShadowGenVertexShader, NULL, 0);
	m_DX11Render->GetDeviceContext()->GSSetShader(m_pPointShadowGenGeometryShader, NULL, 0);
	m_DX11Render->GetDeviceContext()->PSSetShader(NULL, NULL, 0);

	for (unsigned int i = 0; i < m_PointLights.size(); ++i)
	{

		m_PointLights[i]->m_ShadowMapViewProj = PEMatrix::MakePerspectiveMatrix(PracEng_PI * 0.5f, 1.0f, SHADOWMAP_NEAR, m_PointLights[i]->GetLightRange());

		// Set the depth target
		ID3D11RenderTargetView* nullRT = NULL;
		ID3D11DepthStencilView* pDSV = m_PointDepthStencilDSV[i];
		m_DX11Render->GetDeviceContext()->OMSetRenderTargets(1, &nullRT, pDSV);

		// Clear the depth stencil
		m_DX11Render->GetDeviceContext()->ClearDepthStencilView(pDSV, D3D11_CLEAR_DEPTH, 1.0, 0);

		// Prepare the projection to shadow space for each cube face
		PEMatrix matPointProj;
		matPointProj = PEMatrix::MakePerspectiveMatrix(PracEng_PI * 0.5f, 1.0, SHADOWMAP_NEAR, m_PointLights[i]->GetLightRange());

		// Fill the shadow generation matrices constant buffer
		D3D11_MAPPED_SUBRESOURCE MappedResource;
		m_DX11Render->GetDeviceContext()->Map(m_pPointShadowGenGeometryCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource);
		PEMatrix* pShadowGenMat = (PEMatrix*)MappedResource.pData;
		PEMatrix matSpotPos = PEMatrix::GetIdentityMatrix();
		matSpotPos.SetTranslation(m_PointLights[i]->GetLightPos() * -1.0f);

		PEMatrix matSpotView;
		PEMatrix toShadow;

		// Cube +X
		PEVector4 PitchYawRoll(0.0f, PracEng_PI + PracEng_PIDIV2, 0.0f, 0.0f);
		//matSpotView = PEMatrix::SetRotationYawPitchRoll(PitchYawRoll); 
		matSpotView = PEMatrix::GetIdentityMatrix();
		matSpotView.RotationY(PracEng_PI + PracEng_PIDIV2);
		toShadow = matSpotPos * matSpotView * matPointProj;
		pShadowGenMat[0] = PEMatrix::MakeTranspose(toShadow);
		
		// Cube -X
		//PitchYawRoll = PEVector4(0.0f, PracEng_PIDIV2, 0.0f, 0.0f);
		//matSpotView = PEMatrix::SetRotationYawPitchRoll(PitchYawRoll); 
		matSpotView = PEMatrix::GetIdentityMatrix();
		matSpotView.RotationY(PracEng_PIDIV2);
		toShadow = matSpotPos * matSpotView * matPointProj;
		pShadowGenMat[1] = PEMatrix::MakeTranspose(toShadow);

		// Cube +Y
		PitchYawRoll = PEVector4(PracEng_PIDIV2, 0.0f, 0.0f, 0.0f);
		matSpotView = PEMatrix::SetRotationYawPitchRoll(PitchYawRoll);
		toShadow = matSpotPos * matSpotView * matPointProj;
		pShadowGenMat[2] = PEMatrix::MakeTranspose(toShadow);

		// Cube -Y
		PitchYawRoll = PEVector4(PracEng_PI + PracEng_PIDIV2, 0.0f, 0.0f, 0.0f);
		matSpotView = PEMatrix::SetRotationYawPitchRoll(PitchYawRoll);
		toShadow = matSpotPos * matSpotView * matPointProj;
		pShadowGenMat[3] = PEMatrix::MakeTranspose(toShadow);
		// Cube +Z
		// Identity view
		toShadow = matSpotPos * matPointProj;
		pShadowGenMat[4] = PEMatrix::MakeTranspose(toShadow);
		// Cube -Z
		PitchYawRoll = PEVector4(0.0f, PracEng_PI, 0.0f, 0.0f);
		matSpotView = PEMatrix::SetRotationYawPitchRoll(PitchYawRoll);
		toShadow = matSpotPos * matSpotView * matPointProj;
		pShadowGenMat[5] = PEMatrix::MakeTranspose(toShadow);

		m_DX11Render->GetDeviceContext()->Unmap(m_pPointShadowGenGeometryCB, 0);
		m_DX11Render->GetDeviceContext()->GSSetConstantBuffers(0, 1, &m_pPointShadowGenGeometryCB);

		for (auto& pActor : Actors)
		{
			pActor->Draw(m_DX11Render);
		}
	/*	pDSV = NULL;
		m_DX11Render->GetDeviceContext()->OMSetRenderTargets(1, &nullRT, pDSV);*/
	}

	// Cleanup
	m_DX11Render->GetDeviceContext()->VSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->GSSetShader(NULL, NULL, 0);
	//m_DX11Render->GetDeviceContext()->PSSetShader(NULL, NULL, 0);

}

void SceneManager::GenCascadeShadowMap()
{
	static D3D11_TEXTURE2D_DESC dtd = {
		SHADOWMAP_SIZE_SPOT, //UINT Width;
		SHADOWMAP_SIZE_SPOT, //UINT Height;
		1, //UINT MipLevels;
		CascadeShadowMapMatrixs::m_iTotalCascades, //UINT ArraySize;
		//DXGI_FORMAT_R24G8_TYPELESS,
		DXGI_FORMAT_R32_TYPELESS, //DXGI_FORMAT Format;
		1, //DXGI_SAMPLE_DESC SampleDesc;
		0,
		D3D11_USAGE_DEFAULT,//D3D11_USAGE Usage;
		D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE,//UINT BindFlags;
		0,//UINT CPUAccessFlags;
		0//UINT MiscFlags;    
	};

	static D3D11_DEPTH_STENCIL_VIEW_DESC descDepthView =
	{
		// DXGI_FORMAT_D24_UNORM_S8_UINT,
		DXGI_FORMAT_D32_FLOAT,
		D3D11_DSV_DIMENSION_TEXTURE2D,
		0
	};
	static D3D11_SHADER_RESOURCE_VIEW_DESC descShaderView =
	{
		//DXGI_FORMAT_R24_UNORM_X8_TYPELESS,
		DXGI_FORMAT_R32_FLOAT,
		D3D11_SRV_DIMENSION_TEXTURE2D,
		0,
		0
	};

	descDepthView.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
	descDepthView.Texture2DArray.FirstArraySlice = 0;
	descDepthView.Texture2DArray.ArraySize = CascadeShadowMapMatrixs::m_iTotalCascades;
	
	descShaderView.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
	descShaderView.Texture2DArray.FirstArraySlice = 0;
	descShaderView.Texture2DArray.ArraySize = CascadeShadowMapMatrixs::m_iTotalCascades;
	descShaderView.TextureCube.MipLevels = 1;
	descShaderView.TextureCube.MostDetailedMip = 0;

	int cnt = m_DirectionalLights.size() - m_pCascadedDepthStencilDSV.size();
	if (cnt > 0)
	{
		m_pCascadedDepthStencilDSV.resize(cnt);
		m_pCascadedDepthStencilRT.resize(cnt);
		m_pCascadedDepthStencilSRV.resize(cnt);
		int nowCnt = m_DirectionalLights.size();
		char strResName[128];
		for (unsigned int i = cnt; i > 0; --i)
		{
			int label = nowCnt - i;

			sprintf_s(strResName, "Cascade Shadow map Target %d", label);
			TRACE_HR(m_DX11Render->CreateTexture2D(&dtd, NULL, &m_pCascadedDepthStencilRT[label]));
			SetDebugObjectName(m_pCascadedDepthStencilRT[label], strResName);

			sprintf_s(strResName, "Cascade Shadow map Resource View %d", label);
			TRACE_HR(m_DX11Render->CreateSRView(m_pCascadedDepthStencilRT[label], &descShaderView, &m_pCascadedDepthStencilSRV[label]));
			SetDebugObjectName(m_pCascadedDepthStencilSRV[label], strResName);

			sprintf_s(strResName, "Cascade Shadow map Depth View %d", label);
			TRACE_HR(m_DX11Render->CreateDSView(m_pCascadedDepthStencilRT[label], &descDepthView, &m_pCascadedDepthStencilDSV[label]));
			SetDebugObjectName(m_pCascadedDepthStencilDSV[label], strResName);
		}
	}

	D3D11_VIEWPORT vp[3] = { { 0, 0, SHADOWMAP_SIZE_SPOT, SHADOWMAP_SIZE_SPOT, 0.0f, 1.0f }, { 0, 0, SHADOWMAP_SIZE_SPOT, SHADOWMAP_SIZE_SPOT, 0.0f, 1.0f }, { 0, 0, SHADOWMAP_SIZE_SPOT, SHADOWMAP_SIZE_SPOT, 0.0f, 1.0f } };
	m_DX11Render->GetDeviceContext()->RSSetViewports(3, vp);

	m_DX11Render->GetDeviceContext()->IASetInputLayout(m_pShadowGenVSLayout);

	m_DX11Render->GetDeviceContext()->VSSetShader(m_pCascadedShadowGenVertexShader, NULL, 0);
	m_DX11Render->GetDeviceContext()->GSSetShader(m_pCascadedShadowGenGeometryShader, NULL, 0);
	m_DX11Render->GetDeviceContext()->PSSetShader(NULL, NULL, 0);

	for (unsigned int i = 0; i < m_DirectionalLights.size(); ++i)
	{
		// Set the depth target
		ID3D11RenderTargetView* nullRT = NULL;
		m_DX11Render->GetDeviceContext()->OMSetRenderTargets(1, &nullRT, m_pCascadedDepthStencilDSV[i]);

		// Clear the depth stencil
		m_DX11Render->GetDeviceContext()->ClearDepthStencilView(m_pCascadedDepthStencilDSV[i], D3D11_CLEAR_DEPTH, 1.0, 0);

		// Get the cascade matrices for the current camera configuration
		PEVector3 DirLit = m_DirectionalLights[i]->GetLightDir();
		m_CascadedMatrix.Update(DirLit);

		// Fill the shadow generation matrices constant buffer
		D3D11_MAPPED_SUBRESOURCE MappedResource;
		m_DX11Render->GetDeviceContext()->Map(m_pCascadedShadowGenGeometryCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource);
		PEMatrix* pCascadeShadowGenMat = (PEMatrix*)MappedResource.pData;

		for (int i = 0; i < CascadeShadowMapMatrixs::m_iTotalCascades; i++)
		{
			const PEMatrix* w2c = m_CascadedMatrix.GetWorldToCascadeProj(i);
			pCascadeShadowGenMat[i] = PEMatrix::MakeTranspose(*w2c);
		}

		m_DX11Render->GetDeviceContext()->Unmap(m_pCascadedShadowGenGeometryCB, 0);
		m_DX11Render->GetDeviceContext()->GSSetConstantBuffers(0, 1, &m_pCascadedShadowGenGeometryCB);

		for (auto& pActor : Actors)
		{
			pActor->Draw(m_DX11Render);
		}

	}
	
	// Cleanup
	m_DX11Render->GetDeviceContext()->VSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->GSSetShader(NULL, NULL, 0);
	//m_DX11Render->GetDeviceContext()->PSSetShader(NULL, NULL, 0);
}

void SceneManager::DoPostProcessing()
{
	HDRToLDR();
	ID3D11RenderTargetView* pRTV = m_DX11Render->GetRenderTargetView();
	m_DX11Render->GetDeviceContext()->OMSetRenderTargets(1, &pRTV, GetDepthDSV());
}

void SceneManager::HDRToLDR()
{
	DownSampleRT();
	CalcLum();
	Adaption();
}

void SceneManager::DownSampleRT()
{
	ID3D11RenderTargetView* rt[1] = { NULL };
	m_DX11Render->GetDeviceContext()->OMSetRenderTargets(1, rt, NULL);
	ID3D11UnorderedAccessView* arrUAVs[2] = { m_pDownScale1DUAV, m_pDownScaleUAV };
	m_DX11Render->GetDeviceContext()->CSSetUnorderedAccessViews(0, 2, arrUAVs, NULL);
	ID3D11ShaderResourceView* arrViews[2] = { m_HDRSRV, NULL };
	m_DX11Render->GetDeviceContext()->CSSetShaderResources(0, 1, arrViews);
	D3D11_MAPPED_SUBRESOURCE MappedResource;
	m_DX11Render->GetDeviceContext()->Map(m_pDownScaleCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource);
	CB_DOWN_SCALE* pDownScale = (CB_DOWN_SCALE*)MappedResource.pData;
	pDownScale->nWidth = m_DX11Render->GetBackBufferWidth() / 4;
	pDownScale->nHeight = m_DX11Render->GetBackBufferHeight() / 4;
	pDownScale->nTotalPixels = pDownScale->nWidth * pDownScale->nHeight;
	pDownScale->nGroupSize = m_nDownScaleGroups;
	pDownScale->fAdaptation = m_fAdaptation;
	pDownScale->fBloomThreshold = m_fBloomThreshold;
	m_DX11Render->GetDeviceContext()->Unmap(m_pDownScaleCB, 0);
	ID3D11Buffer* arrConstBuffers[1] = { m_pDownScaleCB };
	m_DX11Render->GetDeviceContext()->CSSetConstantBuffers(0, 1, arrConstBuffers);
	m_DX11Render->GetDeviceContext()->CSSetShader(m_pDownScaleFirstPassCS, NULL, 0);
	m_DX11Render->GetDeviceContext()->Dispatch(m_nDownScaleGroups, 1, 1);

	ZeroMemory(arrUAVs, sizeof(arrUAVs));
	arrUAVs[0] = m_pAvgLumUAV;
	m_DX11Render->GetDeviceContext()->CSSetUnorderedAccessViews(0, 2, arrUAVs, NULL);

	arrViews[0] = m_pDownScale1DSRV;
	arrViews[1] = m_pPrevAvgLumSRV;
	m_DX11Render->GetDeviceContext()->CSSetShaderResources(0, 2, arrViews);
	m_DX11Render->GetDeviceContext()->CSSetShader(m_pDownScaleSecondPassCS, NULL, 0);
	m_DX11Render->GetDeviceContext()->Dispatch(1, 1, 1);

	m_DX11Render->GetDeviceContext()->CSSetShader(NULL, NULL, 0);

	ZeroMemory(arrViews, sizeof(arrViews));
	m_DX11Render->GetDeviceContext()->CSSetShaderResources(0, 2, arrViews);
	ZeroMemory(arrUAVs, sizeof(arrUAVs));
	m_DX11Render->GetDeviceContext()->CSSetUnorderedAccessViews(0, 2, arrUAVs, (UINT*)(&arrUAVs));

	Bloom();
	Blur();

	ZeroMemory(arrConstBuffers, sizeof(arrConstBuffers));
	m_DX11Render->GetDeviceContext()->CSSetConstantBuffers(0, 1, arrConstBuffers);
}

void SceneManager::CalcLum()
{
	ID3D11RenderTargetView* pRTV = m_DX11Render->GetRenderTargetView();
	m_DX11Render->GetDeviceContext()->OMSetRenderTargets(1, &pRTV, NULL);

	ID3D11ShaderResourceView* arrViews[6] = { m_HDRSRV, m_pAvgLumSRV, m_pBloomSRV, m_pDownScaleSRV, m_DepthStencilSRV };
	m_DX11Render->GetDeviceContext()->PSSetShaderResources(0, 6, arrViews);

	D3D11_MAPPED_SUBRESOURCE MappedResource;
	m_DX11Render->GetDeviceContext()->Map(m_pFinalPassCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource);
	CB_HDR_TO_LDR* pFinalPass = (CB_HDR_TO_LDR*)MappedResource.pData;
	pFinalPass->fMiddleGrey = m_fMidGray;
	pFinalPass->fLumWhiteSqr = m_fWhite;
	pFinalPass->fLumWhiteSqr *= pFinalPass->fMiddleGrey; // Scale by the middle grey value
	pFinalPass->fLumWhiteSqr *= pFinalPass->fLumWhiteSqr; // Squre
	pFinalPass->fBloomScale = m_fBloomScale;
	pFinalPass->fDOFFarStart = m_fDOFFarStart;
	pFinalPass->fDOFFarRangeRcp = m_fDOFFarRanceRcp;
	float fQ = m_Camera->GetFarClip() / (m_Camera->GetFarClip() - m_Camera->GetNearClip());
	pFinalPass->ProjectionValues[0] = -m_Camera->GetNearClip() * fQ;
	pFinalPass->ProjectionValues[1] = -fQ;
	m_DX11Render->GetDeviceContext()->Unmap(m_pFinalPassCB, 0);
	ID3D11Buffer* arrConstBuffers[1] = { m_pFinalPassCB };
	m_DX11Render->GetDeviceContext()->PSSetConstantBuffers(0, 1, arrConstBuffers);

	m_DX11Render->GetDeviceContext()->IASetInputLayout(NULL);
	m_DX11Render->GetDeviceContext()->IASetVertexBuffers(0, 0, NULL, NULL, NULL);
	m_DX11Render->GetDeviceContext()->IASetIndexBuffer(NULL, DXGI_FORMAT_UNKNOWN, 0);
	m_DX11Render->GetDeviceContext()->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);

	ID3D11SamplerState* arrSamplers[2] = { m_pSampPoint, m_pSampLinear };
	m_DX11Render->GetDeviceContext()->PSSetSamplers(0, 2, arrSamplers);

	// Set the shaders
	m_DX11Render->GetDeviceContext()->VSSetShader(m_pFullScreenQuadVS, NULL, 0);
	m_DX11Render->GetDeviceContext()->PSSetShader(m_pFinalPassPS, NULL, 0);

	m_DX11Render->GetDeviceContext()->Draw(4, 0);

	// Cleanup
	ZeroMemory(arrViews, sizeof(arrViews));
	m_DX11Render->GetDeviceContext()->PSSetShaderResources(0, 6, arrViews);
	ZeroMemory(arrConstBuffers, sizeof(arrConstBuffers));
	m_DX11Render->GetDeviceContext()->PSSetConstantBuffers(0, 1, arrConstBuffers);
	m_DX11Render->GetDeviceContext()->VSSetShader(NULL, NULL, 0);
	m_DX11Render->GetDeviceContext()->PSSetShader(NULL, NULL, 0);
}

void SceneManager::Adaption()
{
	ID3D11Buffer* pTempBuffer = m_pAvgLumBuffer;
	ID3D11UnorderedAccessView* pTempUAV = m_pAvgLumUAV;
	ID3D11ShaderResourceView* p_TempSRV = m_pAvgLumSRV;
	m_pAvgLumBuffer = m_pPrevAvgLumBuffer;
	m_pAvgLumUAV = m_pPrevAvgLumUAV;
	m_pAvgLumSRV = m_pPrevAvgLumSRV;
	m_pPrevAvgLumBuffer = pTempBuffer;
	m_pPrevAvgLumUAV = pTempUAV;
	m_pPrevAvgLumSRV = p_TempSRV;
}

void SceneManager::Bloom()
{
	ID3D11ShaderResourceView* arrViews[2] = { m_pDownScaleSRV, m_pAvgLumSRV };
	m_DX11Render->GetDeviceContext()->CSSetShaderResources(0, 2, arrViews);

	ID3D11UnorderedAccessView* arrUAVs[1] = { m_pTempUAV[0] };
	m_DX11Render->GetDeviceContext()->CSSetUnorderedAccessViews(0, 1, arrUAVs, NULL);

	m_DX11Render->GetDeviceContext()->CSSetShader(m_pBloomRevealCS, NULL, 0);
	m_DX11Render->GetDeviceContext()->Dispatch(m_nDownScaleGroups, 1, 1);

	m_DX11Render->GetDeviceContext()->CSSetShader(NULL, NULL, 0);
	ZeroMemory(arrViews, sizeof(arrViews));
	m_DX11Render->GetDeviceContext()->CSSetShaderResources(0, 2, arrViews);
	ZeroMemory(arrUAVs, sizeof(arrUAVs));
	m_DX11Render->GetDeviceContext()->CSSetUnorderedAccessViews(0, 1, arrUAVs, NULL);
}

void SceneManager::Blur()
{
	ID3D11UnorderedAccessView* arrUAVs[1] = { m_pTempUAV[1] };
	m_DX11Render->GetDeviceContext()->CSSetUnorderedAccessViews(0, 1, arrUAVs, NULL);

	ID3D11ShaderResourceView* arrViews[1] = { m_pTempSRV[0] };
	m_DX11Render->GetDeviceContext()->CSSetShaderResources(0, 1, arrViews);
	m_DX11Render->GetDeviceContext()->CSSetShader(m_HorizontalBlurCS, NULL, 0);
	m_DX11Render->GetDeviceContext()->Dispatch((UINT)ceil((m_DX11Render->GetBackBufferWidth() / 4.0f) / (128.0f - 12.0f)), (UINT)ceil(m_DX11Render->GetBackBufferHeight() / 4.0f), 1);
	arrUAVs[0] = m_pBloomUAV;
	m_DX11Render->GetDeviceContext()->CSSetUnorderedAccessViews(0, 1, arrUAVs, NULL);
	
	arrViews[0] = m_pTempSRV[1];
	m_DX11Render->GetDeviceContext()->CSSetShaderResources(0, 1, arrViews);
	m_DX11Render->GetDeviceContext()->CSSetShader(m_VerticalBlurCS, NULL, 0);
	m_DX11Render->GetDeviceContext()->Dispatch((UINT)ceil(m_DX11Render->GetBackBufferWidth() / 4.0f), (UINT)ceil((m_DX11Render->GetBackBufferHeight() / 4.0f) / (128.0f - 12.0f)), 1);

	m_DX11Render->GetDeviceContext()->CSSetShader(NULL, NULL, 0);
	ZeroMemory(arrViews, sizeof(arrViews));
	m_DX11Render->GetDeviceContext()->CSSetShaderResources(0, 1, arrViews);
	ZeroMemory(arrUAVs, sizeof(arrUAVs));
	m_DX11Render->GetDeviceContext()->CSSetUnorderedAccessViews(0, 1, arrUAVs, NULL);
}

void SceneManager::DoSSAO()
{
	ID3D11ShaderResourceView *arrSRV[1] = { NULL };
	m_DX11Render->GetDeviceContext()->PSSetShaderResources(5, 1, arrSRV);

	//DownScaleDepth
	int iSSAO_Width = m_DX11Render->GetBackBufferWidth() / 2;
	int iSSAO_Height = m_DX11Render->GetBackBufferHeight() / 2;
	D3D11_MAPPED_SUBRESOURCE MappedResource;
	m_DX11Render->GetDeviceContext()->Map(m_pSSAODownscaleCB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource);
	CB_SSAO_DOWN_SCALE* pDownscale = (CB_SSAO_DOWN_SCALE*)MappedResource.pData;
	pDownscale->nWidth = iSSAO_Width;
	pDownscale->nHeight = iSSAO_Height;
	pDownscale->fHorResRcp = 1.0f / (float)pDownscale->nWidth;
	pDownscale->fVerResRcp = 1.0f / (float)pDownscale->nHeight;
	const PEMatrix* pProj = m_Camera->GetProjectMatrix();
	pDownscale->ProjParams.x = 1.0f / pProj->m[0][0];
	pDownscale->ProjParams.y = 1.0f / pProj->m[1][1];
	float fQ = m_Camera->GetFarClip() / (m_Camera->GetFarClip() - m_Camera->GetNearClip());
	pDownscale->ProjParams.z = -m_Camera->GetNearClip() * fQ;
	pDownscale->ProjParams.w = -fQ;
	pDownscale->ViewMatrix = PEMatrix::MakeTranspose(*m_Camera->GetViewMatrix());
	pDownscale->fOffsetRadius = (float)m_iSSAOSampRadius;
	pDownscale->fRadius = m_fSSAORadius;
	pDownscale->fMaxDepth = m_Camera->GetFarClip();
	m_DX11Render->GetDeviceContext()->Unmap(m_pSSAODownscaleCB, 0);
	ID3D11Buffer* arrConstBuffers[1] = { m_pSSAODownscaleCB };
	m_DX11Render->GetDeviceContext()->CSSetConstantBuffers(0, 1, arrConstBuffers);

	// Output
	ID3D11UnorderedAccessView* arrUAVs[1] = { m_pMiniDepthUAV };
	m_DX11Render->GetDeviceContext()->CSSetUnorderedAccessViews(0, 1, arrUAVs, NULL);

	// Input
	ID3D11ShaderResourceView* arrViews[2] = { GetDepthView(), GetNormalView() };
	m_DX11Render->GetDeviceContext()->CSSetShaderResources(0, 2, arrViews);

	// Shader
	m_DX11Render->GetDeviceContext()->CSSetShader(m_pSSAODepthDownscaleCS, NULL, 0);

	// Execute the downscales first pass with enough groups to cover the entire full res HDR buffer
	m_DX11Render->GetDeviceContext()->Dispatch((UINT)ceil((float)(iSSAO_Height * iSSAO_Width) / 1024.0f), 1, 1);

	// Cleanup
	m_DX11Render->GetDeviceContext()->CSSetShader(NULL, NULL, 0);
	ZeroMemory(arrViews, sizeof(arrViews));
	m_DX11Render->GetDeviceContext()->CSSetShaderResources(0, 2, arrViews);
	ZeroMemory(arrUAVs, sizeof(arrUAVs));
	m_DX11Render->GetDeviceContext()->CSSetUnorderedAccessViews(0, 1, arrUAVs, NULL);
	ZeroMemory(arrConstBuffers, sizeof(arrConstBuffers));
	m_DX11Render->GetDeviceContext()->CSSetConstantBuffers(0, 1, arrConstBuffers);

	//computer SSAO
	// Constants
	arrConstBuffers[0] =  m_pSSAODownscaleCB ;
	m_DX11Render->GetDeviceContext()->CSSetConstantBuffers(0, 1, arrConstBuffers);

	// Output
	arrUAVs[0] =  m_pSSAO_UAV ;
	m_DX11Render->GetDeviceContext()->CSSetUnorderedAccessViews(0, 1, arrUAVs, NULL);

	// Input
	arrViews[0] =  m_pMiniDepthSRV ;
	m_DX11Render->GetDeviceContext()->CSSetShaderResources(0, 1, arrViews);

	// Shader
	m_DX11Render->GetDeviceContext()->CSSetShader(m_pSSAOComputeCS, NULL, 0);

	// Execute the downscales first pass with enough groups to cover the entire full res HDR buffer
	m_DX11Render->GetDeviceContext()->Dispatch((UINT)ceil((float)(iSSAO_Height * iSSAO_Width) / 1024.0f), 1, 1);

	// Cleanup
	m_DX11Render->GetDeviceContext()->CSSetShader(NULL, NULL, 0);
	ZeroMemory(arrViews, sizeof(arrViews));
	m_DX11Render->GetDeviceContext()->CSSetShaderResources(0, 1, arrViews);
	ZeroMemory(arrUAVs, sizeof(arrUAVs));
	m_DX11Render->GetDeviceContext()->CSSetUnorderedAccessViews(0, 1, arrUAVs, NULL);
	ZeroMemory(arrConstBuffers, sizeof(arrConstBuffers));
	m_DX11Render->GetDeviceContext()->CSSetConstantBuffers(0, 1, arrConstBuffers);


	//
	arrSRV[0] = m_pSSAO_SRV;
	m_DX11Render->GetDeviceContext()->PSSetShaderResources(5, 1, arrSRV);
}


void DirectionalLight::SetLightDir(const PEVector3& Direction)
{
	m_Dir = Direction;
}

PEVector3 DirectionalLight::GetLightDir()
{
	return m_Dir;
}

void DirectionalLight::SetLightDir(float x, float y, float z)
{
	m_Dir = PEVector3(x, y, z);
}

void PointLight::SetLightRange(float Radius)
{
	m_Range = Radius;
}

float PointLight::GetLightRange()
{
	return m_Range;
}

void Light::SetLightPos(const PEVector3& Pos)
{
	m_LightPosition = Pos;
}

void Light::SetLightPos(float x, float y, float z)
{
	m_LightPosition.x = x;
	m_LightPosition.y = y;
	m_LightPosition.z = z;
}

void Light::SetLightColor(float R, float G, float B)
{
	m_LightColor = PEVector3(R, G, B);
}

void Light::SetLightColor(const PEVector3& color)
{
	m_LightColor = color;
}

void SpotLight::SetLightDir(const PEVector3& Direction)
{
	m_Dir = Direction;
}

void SpotLight::SetLightDir(float x, float y, float z)
{
	m_Dir.x = x;
	m_Dir.y = y;
	m_Dir.z = z;
}

PEVector3 SpotLight::GetLightDir()
{
	return m_Dir;
}

void SpotLight::SetLightRange(float Radius)
{
	m_Range = Radius;
}

float SpotLight::GetLightRange()
{
	return m_Range;
}

float SpotLight::GetInnerAngle()
{
	return m_InnerAngle;
}

float SpotLight::GetOuterAngle()
{
	return m_OuterAngle;
}

void SpotLight::SetAngle(float fInnerAngle, float fOutAngle)
{
	m_InnerAngle = fInnerAngle*PracEng_PI / 180.0f;
	m_OuterAngle = fOutAngle*PracEng_PI / 180.0f;
}

CascadeShadowMapMatrixs::CascadeShadowMapMatrixs() : m_bAntiFlickerOn(true), m_fCascadeTotalRange(1.0f), m_fShadowBoundRadius(.0f)
{

}

CascadeShadowMapMatrixs::~CascadeShadowMapMatrixs()
{

}

void CascadeShadowMapMatrixs::Init(int iShadowMapSize, Camera* pCam)
{
	m_iShadowMapSize = iShadowMapSize;
	m_Cam = pCam;
	// Set the range values
	m_arrCascadeRanges[0] = m_Cam->GetNearClip();
	m_arrCascadeRanges[1] = .4f;
	m_arrCascadeRanges[2] = .8f;
	m_arrCascadeRanges[3] = m_fCascadeTotalRange;

	for (int i = 0; i < m_iTotalCascades; i++)
	{
		m_arrCascadeBoundCenter[i] = PEVector3(0.0f, 0.0f, 0.0f);
		m_arrCascadeBoundRadius[i] = 0.0f;
	}
}

void CascadeShadowMapMatrixs::Update(const PEVector3& vDirectionalDir)
{
	// Find the view matrix
	PEVector4 CamDir = *m_Cam->GetCamToward();
	PEVector3 vWorldCenter = *m_Cam->GetEyeLoc() + CamDir.Vec3() * m_fCascadeTotalRange * 0.5f;
	PEVector3 vPos = vWorldCenter;
	PEVector3 vLookAt = vWorldCenter + vDirectionalDir * m_Cam->GetFarClip();
	
	PEVector3 vRight = PEVector3(1.0f, 0.0f, 0.0f);
	PEVector3 vUp = PEVector3::Cross(vDirectionalDir, vRight);
	vUp.Normalize();
	PEMatrix mShadowView = PEMatrix::MakeLookAtLHMatrix(&vPos, &vLookAt, &vUp);

	// Get the bounds for the shadow space
	float fRadius;
	ExtractFrustumBoundSphere(m_arrCascadeRanges[0], m_arrCascadeRanges[3], m_vShadowBoundCenter, fRadius);
	m_fShadowBoundRadius = max(m_fShadowBoundRadius, fRadius); // Expend the radius to compensate for numerical errors
	
	// Find the projection matrix
	PEMatrix mShadowProj = PEMatrix::MakeOrthoMatrix(m_fShadowBoundRadius, m_fShadowBoundRadius, -m_fShadowBoundRadius, m_fShadowBoundRadius);
	

	// The combined transformation from world to shadow space
	m_WorldToShadowSpace = mShadowView * mShadowProj;

	// For each cascade find the transformation from shadow to cascade space
	PEMatrix mShadowViewInv = PEMatrix::MakeTranspose(mShadowView);
	for (int iCascadeIdx = 0; iCascadeIdx < m_iTotalCascades; iCascadeIdx++)
	{
		PEMatrix cascadeTrans;
		PEMatrix cascadeScale;
		if (m_bAntiFlickerOn)
		{
			// To avoid anti flickering we need to make the transformation invariant to camera rotation and translation
			// By encapsulating the cascade frustum with a sphere we achive the rotation invariance
			PEVector3 vNewCenter;
			ExtractFrustumBoundSphere(m_arrCascadeRanges[iCascadeIdx], m_arrCascadeRanges[iCascadeIdx + 1], vNewCenter, fRadius);
			m_arrCascadeBoundRadius[iCascadeIdx] = max(m_arrCascadeBoundRadius[iCascadeIdx], fRadius); // Expend the radius to compensate for numerical errors

			// Only update the cascade bounds if it moved at least a full pixel unit
			// This makes the transformation invariant to translation
			PEVector3 vOffset;
			if (CascadeNeedsUpdate(mShadowView, iCascadeIdx, vNewCenter, vOffset))
			{
				// To avoid flickering we need to move the bound center in full units
				PEVector3 vOffsetOut = mShadowViewInv * vOffset;
				m_arrCascadeBoundCenter[iCascadeIdx] += vOffsetOut;
			}

			// Get the cascade center in shadow space
			PEVector3 vCascadeCenterShadowSpace = m_WorldToShadowSpace * m_arrCascadeBoundCenter[iCascadeIdx];

			// Update the translation from shadow to cascade space
			m_vToCascadeOffsetX[iCascadeIdx] = -vCascadeCenterShadowSpace.x;
			m_vToCascadeOffsetY[iCascadeIdx] = -vCascadeCenterShadowSpace.y;
			cascadeTrans = PEMatrix::GetIdentityMatrix();
			cascadeTrans.SetTranslation(m_vToCascadeOffsetX[iCascadeIdx], m_vToCascadeOffsetY[iCascadeIdx], .0f);

			// Update the scale from shadow to cascade space
			m_vToCascadeScale[iCascadeIdx] = m_fShadowBoundRadius / m_arrCascadeBoundRadius[iCascadeIdx];
			cascadeScale = PEMatrix::GetIdentityMatrix();
			cascadeScale.Scale(m_vToCascadeScale[iCascadeIdx], m_vToCascadeScale[iCascadeIdx], 1.0f);

		}
		else
		{
			// Since we don't care about flickering we can make the cascade fit tightly around the frustum
			// Extract the bounding box
			PEVector3 arrFrustumPoints[8];
			ExtractFrustumPoints(m_arrCascadeRanges[iCascadeIdx], m_arrCascadeRanges[iCascadeIdx + 1], arrFrustumPoints);

			// Transform to shadow space and extract the minimum andn maximum
			PEVector3 vMin = PEVector3(FLT_MAX, FLT_MAX, FLT_MAX);
			PEVector3 vMax = PEVector3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
			for (int i = 0; i < 8; i++)
			{
				PEVector3 vPointInShadowSpace = m_WorldToShadowSpace * arrFrustumPoints[i];

				for (int j = 0; j < 3; j++)
				{
					if ((&vMin.x)[j] >(&vPointInShadowSpace.x)[j])
						(&vMin.x)[j] = (&vPointInShadowSpace.x)[j];
					if ((&vMax.x)[j] < (&vPointInShadowSpace.x)[j])
						(&vMax.x)[j] = (&vPointInShadowSpace.x)[j];
				}
			}

			PEVector3 vCascadeCenterShadowSpace = (vMin + vMax) * 0.5f;

			// Update the translation from shadow to cascade space
			m_vToCascadeOffsetX[iCascadeIdx] = -vCascadeCenterShadowSpace.x;
			m_vToCascadeOffsetY[iCascadeIdx] = -vCascadeCenterShadowSpace.y;
			cascadeTrans = PEMatrix::GetIdentityMatrix();
			cascadeTrans.SetTranslation(m_vToCascadeOffsetX[iCascadeIdx], m_vToCascadeOffsetY[iCascadeIdx], 0.0f);

			// Update the scale from shadow to cascade space
			m_vToCascadeScale[iCascadeIdx] = 2.0f / max(vMax.x - vMin.x, vMax.y - vMin.y);
			cascadeScale = PEMatrix::GetIdentityMatrix();
			cascadeScale.Scale(m_vToCascadeScale[iCascadeIdx], m_vToCascadeScale[iCascadeIdx], 1.0f);
		
		}

		// Combine the matrices to get the transformation from world to cascade space
		m_arrWorldToCascadeProj[iCascadeIdx] = m_WorldToShadowSpace * cascadeTrans * cascadeScale;
	}

	// Set the values for the unused slots to someplace outside the shadow space
	for (int i = m_iTotalCascades; i < 4; i++)
	{
		m_vToCascadeOffsetX[i] = 250.0f;
		m_vToCascadeOffsetY[i] = 250.0f;
		m_vToCascadeScale[i] = 0.1f;
	}
}

void CascadeShadowMapMatrixs::ExtractFrustumPoints(float fNear, float fFar, PEVector3* arrFrustumCorners)
{
	// Get the camera bases
	const PEVector3& camPos = *m_Cam->GetEyeLoc();
	const PEVector3& camForward = *m_Cam->GetCamToward();
	const PEVector3& camUp = *m_Cam->GetCamUp();
	PEVector3& camRight = camUp.Cross(camForward);
	camRight.Normalize();

	// Calculate the tangent values (this can be cached
	const float fTanFOVX = tanf(m_Cam->GetAspectRatio() * m_Cam->GetFOV());
	const float fTanFOVY = tanf(m_Cam->GetAspectRatio());

	// Calculate the points on the near plane
	arrFrustumCorners[0] = camPos + (-camRight * fTanFOVX + camUp * fTanFOVY + camForward) * fNear;
	arrFrustumCorners[1] = camPos + (camRight * fTanFOVX + camUp * fTanFOVY + camForward) * fNear;
	arrFrustumCorners[2] = camPos + (camRight * fTanFOVX - camUp * fTanFOVY + camForward) * fNear;
	arrFrustumCorners[3] = camPos + (-camRight * fTanFOVX - camUp * fTanFOVY + camForward) * fNear;

	// Calculate the points on the far plane
	arrFrustumCorners[4] = camPos + (-camRight * fTanFOVX + camUp * fTanFOVY + camForward) * fFar;
	arrFrustumCorners[5] = camPos + (camRight * fTanFOVX + camUp * fTanFOVY + camForward) * fFar;
	arrFrustumCorners[6] = camPos + (camRight * fTanFOVX - camUp * fTanFOVY + camForward) * fFar;
	arrFrustumCorners[7] = camPos + (-camRight * fTanFOVX - camUp * fTanFOVY + camForward) * fFar;
}

void CascadeShadowMapMatrixs::ExtractFrustumBoundSphere(float fNear, float fFar, PEVector3& vBoundCenter, float& fBoundRadius)
{
	// Get the camera bases
	const PEVector3& camPos = *m_Cam->GetEyeLoc();
	const PEVector3& camUp = *m_Cam->GetCamUp();
	const PEVector3& camForward = *m_Cam->GetCamToward();
	const PEVector3& camRight = PEVector3::Cross(camUp, camForward);
	
	

	// Calculate the tangent values (this can be cached as long as the FOV doesn't change)
	const float fTanFOVX = tanf(m_Cam->GetAspectRatio() * m_Cam->GetFOV());
	const float fTanFOVY = tanf(m_Cam->GetAspectRatio());

	// The center of the sphere is in the center of the frustum
	vBoundCenter = camPos + camForward * (fNear + 0.5f * (fNear + fFar));

	// Radius is the distance to one of the frustum far corners
	const PEVector3 vBoundSpan = camPos + (-camRight * fTanFOVX + camUp * fTanFOVY + camForward) * fFar - vBoundCenter;
	fBoundRadius = vBoundSpan.Length();
}

bool CascadeShadowMapMatrixs::CascadeNeedsUpdate(const PEMatrix& mShadowView, int iCascadeIdx, const PEVector3& newCenter, PEVector3& vOffset)
{
	// Find the offset between the new and old bound ceter
	PEVector3 vOldCenterInCascade = mShadowView * m_arrCascadeBoundCenter[iCascadeIdx];
	PEVector3 vNewCenterInCascade = mShadowView * newCenter;
	
	PEVector3 vCenterDiff = vNewCenterInCascade - vOldCenterInCascade;

	// Find the pixel size based on the diameters and map pixel size
	float fPixelSize = (float)m_iShadowMapSize / (2.0f * m_arrCascadeBoundRadius[iCascadeIdx]);

	float fPixelOffX = vCenterDiff.x * fPixelSize;
	float fPixelOffY = vCenterDiff.y * fPixelSize;

	// Check if the center moved at least half a pixel unit
	bool bNeedUpdate = abs(fPixelOffX) > 0.5f || abs(fPixelOffY) > 0.5f;
	if (bNeedUpdate)
	{
		// Round to the 
		vOffset.x = floorf(0.5f + fPixelOffX) / fPixelSize;
		vOffset.y = floorf(0.5f + fPixelOffY) / fPixelSize;
		vOffset.z = vCenterDiff.z;
	}

	return bNeedUpdate;
}
